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
parent
commit
b5b7af7741
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      CHANGELOG.md
  2. 4
      apps/api/src/app/admin/admin.service.ts
  3. 6
      apps/api/src/app/admin/update-asset-profile.dto.ts
  4. 3
      apps/api/src/services/symbol-profile.service.ts
  5. 8
      apps/client/src/app/components/admin-market-data/admin-market-data.component.ts
  6. 16
      apps/client/src/app/components/admin-market-data/admin-market-data.html
  7. 5
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  8. 12
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
  9. 3
      apps/client/src/app/services/admin.service.ts
  10. 12
      apps/client/src/app/services/data.service.ts
  11. 1
      libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts
  12. 38
      libs/ui/src/lib/activities-table/activities-table.component.html
  13. 1
      libs/ui/src/lib/activities-table/activities-table.component.ts
  14. 2
      prisma/migrations/20221128064317_added_comment_to_symbol_profile/migration.sql
  15. 1
      prisma/schema.prisma

12
CHANGELOG.md

@ -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/), 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). 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 ## 1.215.0 - 2022-11-27
### Changed ### Changed
@ -538,7 +548,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Support a note for activities - Supported a note for activities
### Todo ### Todo

4
apps/api/src/app/admin/admin.service.ts

@ -116,6 +116,7 @@ export class AdminService {
}, },
assetClass: true, assetClass: true,
assetSubClass: true, assetSubClass: true,
comment: true,
countries: true, countries: true,
dataSource: true, dataSource: true,
Order: { Order: {
@ -150,6 +151,7 @@ export class AdminService {
activitiesCount: symbolProfile._count.Order, activitiesCount: symbolProfile._count.Order,
assetClass: symbolProfile.assetClass, assetClass: symbolProfile.assetClass,
assetSubClass: symbolProfile.assetSubClass, assetSubClass: symbolProfile.assetSubClass,
comment: symbolProfile.comment,
dataSource: symbolProfile.dataSource, dataSource: symbolProfile.dataSource,
date: symbolProfile.Order?.[0]?.date, date: symbolProfile.Order?.[0]?.date,
symbol: symbolProfile.symbol symbol: symbolProfile.symbol
@ -190,11 +192,13 @@ export class AdminService {
} }
public async patchAssetProfileData({ public async patchAssetProfileData({
comment,
dataSource, dataSource,
symbol, symbol,
symbolMapping symbolMapping
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) { }: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
await this.symbolProfileService.updateSymbolProfile({ await this.symbolProfileService.updateSymbolProfile({
comment,
dataSource, dataSource,
symbol, symbol,
symbolMapping symbolMapping

6
apps/api/src/app/admin/update-asset-profile.dto.ts

@ -1,6 +1,10 @@
import { IsObject, IsOptional } from 'class-validator'; import { IsObject, IsOptional, IsString } from 'class-validator';
export class UpdateAssetProfileDto { export class UpdateAssetProfileDto {
@IsString()
@IsOptional()
comment?: string;
@IsObject() @IsObject()
@IsOptional() @IsOptional()
symbolMapping?: { symbolMapping?: {

3
apps/api/src/services/symbol-profile.service.ts

@ -104,12 +104,13 @@ export class SymbolProfileService {
} }
public updateSymbolProfile({ public updateSymbolProfile({
comment,
dataSource, dataSource,
symbol, symbol,
symbolMapping symbolMapping
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) { }: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
return this.prismaService.symbolProfile.update({ return this.prismaService.symbolProfile.update({
data: { symbolMapping }, data: { comment, symbolMapping },
where: { dataSource_symbol: { dataSource, symbol } } where: { dataSource_symbol: { dataSource, symbol } }
}); });
} }

8
apps/client/src/app/components/admin-market-data/admin-market-data.component.ts

@ -16,6 +16,7 @@ import { UserService } from '@ghostfolio/client/services/user/user.service';
import { DATE_FORMAT, getDateFormatString } from '@ghostfolio/common/helper'; import { DATE_FORMAT, getDateFormatString } from '@ghostfolio/common/helper';
import { Filter, UniqueAsset, User } from '@ghostfolio/common/interfaces'; import { Filter, UniqueAsset, User } from '@ghostfolio/common/interfaces';
import { AdminMarketDataItem } from '@ghostfolio/common/interfaces/admin-market-data.interface'; 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 } from '@prisma/client';
import { format, parseISO } from 'date-fns'; import { format, parseISO } from 'date-fns';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
@ -44,10 +45,10 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit {
AssetSubClass.PRECIOUS_METAL, AssetSubClass.PRECIOUS_METAL,
AssetSubClass.PRIVATE_EQUITY, AssetSubClass.PRIVATE_EQUITY,
AssetSubClass.STOCK AssetSubClass.STOCK
].map((id) => { ].map((assetSubClass) => {
return { return {
id, id: assetSubClass,
label: id, label: translate(assetSubClass),
type: 'ASSET_SUB_CLASS' type: 'ASSET_SUB_CLASS'
}; };
}); });
@ -67,6 +68,7 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit {
'marketDataItemCount', 'marketDataItemCount',
'sectorsCount', 'sectorsCount',
'countriesCount', 'countriesCount',
'comment',
'actions' 'actions'
]; ];
public filters$ = new Subject<Filter[]>(); public filters$ = new Subject<Filter[]>();

16
apps/client/src/app/components/admin-market-data/admin-market-data.html

@ -100,6 +100,22 @@
</td> </td>
</ng-container> </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"> <ng-container matColumnDef="actions">
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell> <th *matHeaderCellDef class="px-1 text-center" mat-header-cell>
<button <button

5
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -30,6 +30,7 @@ import { AssetProfileDialogParams } from './interfaces/interfaces';
export class AssetProfileDialog implements OnDestroy, OnInit { export class AssetProfileDialog implements OnDestroy, OnInit {
public assetProfile: EnhancedSymbolProfile; public assetProfile: EnhancedSymbolProfile;
public assetProfileForm = this.formBuilder.group({ public assetProfileForm = this.formBuilder.group({
comment: '',
symbolMapping: '' symbolMapping: ''
}); });
public countries: { public countries: {
@ -86,6 +87,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
} }
this.assetProfileForm.setValue({ this.assetProfileForm.setValue({
comment: this.assetProfile?.comment,
symbolMapping: JSON.stringify(this.assetProfile?.symbolMapping) symbolMapping: JSON.stringify(this.assetProfile?.symbolMapping)
}); });
@ -129,7 +131,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
} catch {} } catch {}
const assetProfileData: UpdateAssetProfileDto = { const assetProfileData: UpdateAssetProfileDto = {
symbolMapping symbolMapping,
comment: this.assetProfileForm.controls['comment'].value ?? null
}; };
this.adminService this.adminService

12
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html

@ -148,6 +148,18 @@
></textarea> ></textarea>
</mat-form-field> </mat-form-field>
</div> </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>
<div class="d-flex justify-content-end" mat-dialog-actions> <div class="d-flex justify-content-end" mat-dialog-actions>

3
apps/client/src/app/services/admin.service.ts

@ -127,13 +127,14 @@ export class AdminService {
} }
public patchAssetProfile({ public patchAssetProfile({
comment,
dataSource, dataSource,
symbol, symbol,
symbolMapping symbolMapping
}: UniqueAsset & UpdateAssetProfileDto) { }: UniqueAsset & UpdateAssetProfileDto) {
return this.http.patch<EnhancedSymbolProfile>( return this.http.patch<EnhancedSymbolProfile>(
`/api/v1/admin/profile-data/${dataSource}/${symbol}`, `/api/v1/admin/profile-data/${dataSource}/${symbol}`,
{ symbolMapping } { comment, symbolMapping }
); );
} }

12
apps/client/src/app/services/data.service.ts

@ -360,6 +360,7 @@ export class DataService {
const { const {
ACCOUNT: filtersByAccount, ACCOUNT: filtersByAccount,
ASSET_CLASS: filtersByAssetClass, ASSET_CLASS: filtersByAssetClass,
ASSET_SUB_CLASS: filtersByAssetSubClass,
TAG: filtersByTag TAG: filtersByTag
} = groupBy(filters, (filter) => { } = groupBy(filters, (filter) => {
return filter.type; return filter.type;
@ -387,6 +388,17 @@ export class DataService {
); );
} }
if (filtersByAssetSubClass) {
params = params.append(
'assetSubClasses',
filtersByAssetSubClass
.map(({ id }) => {
return id;
})
.join(',')
);
}
if (filtersByTag) { if (filtersByTag) {
params = params.append( params = params.append(
'tags', 'tags',

1
libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts

@ -8,6 +8,7 @@ export interface EnhancedSymbolProfile {
activitiesCount: number; activitiesCount: number;
assetClass: AssetClass; assetClass: AssetClass;
assetSubClass: AssetSubClass; assetSubClass: AssetSubClass;
comment?: string;
countries: Country[]; countries: Country[];
createdAt: Date; createdAt: Date;
currency: string | null; currency: string | null;

38
libs/ui/src/lib/activities-table/activities-table.component.html

@ -299,6 +299,35 @@
<td *matFooterCellDef class="px-1" mat-footer-cell></td> <td *matFooterCellDef class="px-1" mat-footer-cell></td>
</ng-container> </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"> <ng-container matColumnDef="actions">
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell> <th *matHeaderCellDef class="px-1 text-center" mat-header-cell>
<button <button
@ -345,15 +374,6 @@
</mat-menu> </mat-menu>
</th> </th>
<td *matCellDef="let element" class="px-1 text-center" mat-cell> <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 <button
*ngIf="this.showActions" *ngIf="this.showActions"
class="mx-1 no-min-width px-2" class="mx-1 no-min-width px-2"

1
libs/ui/src/lib/activities-table/activities-table.component.ts

@ -94,6 +94,7 @@ export class ActivitiesTableComponent implements OnChanges, OnDestroy {
'currency', 'currency',
'valueInBaseCurrency', 'valueInBaseCurrency',
'account', 'account',
'comment',
'actions' 'actions'
]; ];

2
prisma/migrations/20221128064317_added_comment_to_symbol_profile/migration.sql

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "SymbolProfile" ADD COLUMN "comment" TEXT;

1
prisma/schema.prisma

@ -112,6 +112,7 @@ model Settings {
model SymbolProfile { model SymbolProfile {
assetClass AssetClass? assetClass AssetClass?
assetSubClass AssetSubClass? assetSubClass AssetSubClass?
comment String?
countries Json? countries Json?
createdAt DateTime @default(now()) createdAt DateTime @default(now())
currency String currency String

Loading…
Cancel
Save