Feature/improve asset profile management (#1485)
* Improve asset profile management (Add note, fix filter) * Update changelog
This commit is contained in:
parent
de3e0fad83
commit
b5b7af7741
12
CHANGELOG.md
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/),
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user