Feature/bulk deletion for asset profiles (#3531)
* Add support for bulk deletion of asset profiles * Update changelog
This commit is contained in:
parent
83b5cfff1f
commit
554136cdcd
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added support for bulk deletion of asset profiles from the market data table in the admin control panel
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Added support for derived currencies in the currency validation
|
- Added support for derived currencies in the currency validation
|
||||||
|
@ -10,6 +10,7 @@ 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 { translate } from '@ghostfolio/ui/i18n';
|
||||||
|
|
||||||
|
import { SelectionModel } from '@angular/cdk/collections';
|
||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
@ -97,6 +98,7 @@ export class AdminMarketDataComponent
|
|||||||
public defaultDateFormat: string;
|
public defaultDateFormat: string;
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
public displayedColumns = [
|
public displayedColumns = [
|
||||||
|
'select',
|
||||||
'nameWithSymbol',
|
'nameWithSymbol',
|
||||||
'dataSource',
|
'dataSource',
|
||||||
'assetClass',
|
'assetClass',
|
||||||
@ -115,6 +117,7 @@ export class AdminMarketDataComponent
|
|||||||
public isUUID = isUUID;
|
public isUUID = isUUID;
|
||||||
public placeholder = '';
|
public placeholder = '';
|
||||||
public pageSize = DEFAULT_PAGE_SIZE;
|
public pageSize = DEFAULT_PAGE_SIZE;
|
||||||
|
public selection: SelectionModel<Partial<SymbolProfile>>;
|
||||||
public totalItems = 0;
|
public totalItems = 0;
|
||||||
public user: User;
|
public user: User;
|
||||||
|
|
||||||
@ -188,6 +191,8 @@ export class AdminMarketDataComponent
|
|||||||
|
|
||||||
this.benchmarks = benchmarks;
|
this.benchmarks = benchmarks;
|
||||||
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
|
this.deviceType = this.deviceService.getDeviceInfo().deviceType;
|
||||||
|
|
||||||
|
this.selection = new SelectionModel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onChangePage(page: PageEvent) {
|
public onChangePage(page: PageEvent) {
|
||||||
@ -198,8 +203,16 @@ export class AdminMarketDataComponent
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteProfileData({ dataSource, symbol }: UniqueAsset) {
|
public onDeleteAssetProfile({ dataSource, symbol }: UniqueAsset) {
|
||||||
this.adminMarketDataService.deleteProfileData({ dataSource, symbol });
|
this.adminMarketDataService.deleteAssetProfile({ dataSource, symbol });
|
||||||
|
}
|
||||||
|
|
||||||
|
public onDeleteAssetProfiles() {
|
||||||
|
this.adminMarketDataService.deleteAssetProfiles(
|
||||||
|
this.selection.selected.map(({ dataSource, symbol }) => {
|
||||||
|
return { dataSource, symbol };
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onGather7Days() {
|
public onGather7Days() {
|
||||||
@ -286,6 +299,8 @@ export class AdminMarketDataComponent
|
|||||||
this.placeholder =
|
this.placeholder =
|
||||||
this.activeFilters.length <= 0 ? $localize`Filter by...` : '';
|
this.activeFilters.length <= 0 ? $localize`Filter by...` : '';
|
||||||
|
|
||||||
|
this.selection.clear();
|
||||||
|
|
||||||
this.adminService
|
this.adminService
|
||||||
.fetchAdminMarketData({
|
.fetchAdminMarketData({
|
||||||
sortColumn,
|
sortColumn,
|
||||||
|
@ -20,6 +20,27 @@
|
|||||||
matSortDirection="asc"
|
matSortDirection="asc"
|
||||||
[dataSource]="dataSource"
|
[dataSource]="dataSource"
|
||||||
>
|
>
|
||||||
|
<ng-container matColumnDef="select">
|
||||||
|
<th *matHeaderCellDef class="px-1" mat-header-cell></th>
|
||||||
|
<td *matCellDef="let element" class="px-1" mat-cell>
|
||||||
|
@if (
|
||||||
|
!(
|
||||||
|
element.activitiesCount !== 0 ||
|
||||||
|
element.isBenchmark ||
|
||||||
|
element.symbol.startsWith(ghostfolioScraperApiSymbolPrefix)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
<mat-checkbox
|
||||||
|
color="primary"
|
||||||
|
[checked]="selection.isSelected(element)"
|
||||||
|
(change)="$event ? selection.toggle(element) : null"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
<ng-container matColumnDef="symbol">
|
<ng-container matColumnDef="symbol">
|
||||||
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
<th *matHeaderCellDef class="px-1" mat-header-cell mat-sort-header>
|
||||||
<ng-container i18n>Symbol</ng-container>
|
<ng-container i18n>Symbol</ng-container>
|
||||||
@ -152,6 +173,13 @@
|
|||||||
<button mat-menu-item (click)="onGatherProfileData()">
|
<button mat-menu-item (click)="onGatherProfileData()">
|
||||||
<ng-container i18n>Gather Profile Data</ng-container>
|
<ng-container i18n>Gather Profile Data</ng-container>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
mat-menu-item
|
||||||
|
[disabled]="!selection.hasValue()"
|
||||||
|
(click)="onDeleteAssetProfiles()"
|
||||||
|
>
|
||||||
|
<ng-container i18n>Delete Asset Profiles</ng-container>
|
||||||
|
</button>
|
||||||
</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>
|
||||||
@ -186,7 +214,7 @@
|
|||||||
element.symbol.startsWith(ghostfolioScraperApiSymbolPrefix)
|
element.symbol.startsWith(ghostfolioScraperApiSymbolPrefix)
|
||||||
"
|
"
|
||||||
(click)="
|
(click)="
|
||||||
onDeleteProfileData({
|
onDeleteAssetProfile({
|
||||||
dataSource: element.dataSource,
|
dataSource: element.dataSource,
|
||||||
symbol: element.symbol
|
symbol: element.symbol
|
||||||
})
|
})
|
||||||
|
@ -4,6 +4,7 @@ import { GfActivitiesFilterComponent } from '@ghostfolio/ui/activities-filter';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||||
import { MatSortModule } from '@angular/material/sort';
|
import { MatSortModule } from '@angular/material/sort';
|
||||||
@ -25,6 +26,7 @@ import { GfCreateAssetProfileDialogModule } from './create-asset-profile-dialog/
|
|||||||
GfCreateAssetProfileDialogModule,
|
GfCreateAssetProfileDialogModule,
|
||||||
GfSymbolModule,
|
GfSymbolModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
|
MatCheckboxModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
MatPaginatorModule,
|
MatPaginatorModule,
|
||||||
MatSortModule,
|
MatSortModule,
|
||||||
|
@ -2,13 +2,13 @@ import { AdminService } from '@ghostfolio/client/services/admin.service';
|
|||||||
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
import { UniqueAsset } from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { takeUntil } from 'rxjs';
|
import { EMPTY, catchError, finalize, forkJoin, takeUntil } from 'rxjs';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminMarketDataService {
|
export class AdminMarketDataService {
|
||||||
public constructor(private adminService: AdminService) {}
|
public constructor(private adminService: AdminService) {}
|
||||||
|
|
||||||
public deleteProfileData({ dataSource, symbol }: UniqueAsset) {
|
public deleteAssetProfile({ dataSource, symbol }: UniqueAsset) {
|
||||||
const confirmation = confirm(
|
const confirmation = confirm(
|
||||||
$localize`Do you really want to delete this asset profile?`
|
$localize`Do you really want to delete this asset profile?`
|
||||||
);
|
);
|
||||||
@ -23,4 +23,31 @@ export class AdminMarketDataService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public deleteAssetProfiles(uniqueAssets: UniqueAsset[]) {
|
||||||
|
const confirmation = confirm(
|
||||||
|
$localize`Do you really want to delete these asset profiles?`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirmation) {
|
||||||
|
const deleteRequests = uniqueAssets.map(({ dataSource, symbol }) => {
|
||||||
|
return this.adminService.deleteProfileData({ dataSource, symbol });
|
||||||
|
});
|
||||||
|
|
||||||
|
forkJoin(deleteRequests)
|
||||||
|
.pipe(
|
||||||
|
catchError(() => {
|
||||||
|
alert($localize`Oops! Could not delete asset profiles.`);
|
||||||
|
|
||||||
|
return EMPTY;
|
||||||
|
}),
|
||||||
|
finalize(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 300);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onDeleteProfileData({ dataSource, symbol }: UniqueAsset) {
|
public onDeleteProfileData({ dataSource, symbol }: UniqueAsset) {
|
||||||
this.adminMarketDataService.deleteProfileData({ dataSource, symbol });
|
this.adminMarketDataService.deleteAssetProfile({ dataSource, symbol });
|
||||||
|
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user