Feature/extend GUI to delete watchlist item (#4624)
* Extend GUI to delete watchlist item * Update changelog
This commit is contained in:
parent
afac9daeab
commit
73f009e43b
@ -5,6 +5,12 @@ 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
|
||||
|
||||
- Added support to delete an asset from the watchlist (experimental)
|
||||
|
||||
## 2.157.1 - 2025-04-29
|
||||
|
||||
### Added
|
||||
|
@ -1,6 +1,10 @@
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { Benchmark, User } from '@ghostfolio/common/interfaces';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
Benchmark,
|
||||
User
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
import { GfBenchmarkComponent } from '@ghostfolio/ui/benchmark';
|
||||
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
|
||||
@ -41,6 +45,7 @@ import { CreateWatchlistItemDialogParams } from './create-watchlist-item-dialog/
|
||||
export class HomeWatchlistComponent implements OnDestroy, OnInit {
|
||||
public deviceType: string;
|
||||
public hasPermissionToCreateWatchlistItem: boolean;
|
||||
public hasPermissionToDeleteWatchlistItem: boolean;
|
||||
public user: User;
|
||||
public watchlist: Benchmark[];
|
||||
|
||||
@ -75,6 +80,10 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit {
|
||||
this.user.permissions,
|
||||
permissions.createWatchlistItem
|
||||
);
|
||||
this.hasPermissionToDeleteWatchlistItem = hasPermission(
|
||||
this.user.permissions,
|
||||
permissions.deleteWatchlistItem
|
||||
);
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
}
|
||||
@ -85,6 +94,20 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit {
|
||||
this.loadWatchlistData();
|
||||
}
|
||||
|
||||
public onWatchlistItemDeleted({
|
||||
dataSource,
|
||||
symbol
|
||||
}: AssetProfileIdentifier) {
|
||||
this.dataService
|
||||
.deleteWatchlistItem({ dataSource, symbol })
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe({
|
||||
next: () => {
|
||||
return this.loadWatchlistData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ngOnDestroy() {
|
||||
this.unsubscribeSubject.next();
|
||||
this.unsubscribeSubject.complete();
|
||||
|
@ -12,8 +12,10 @@
|
||||
<gf-benchmark
|
||||
[benchmarks]="watchlist"
|
||||
[deviceType]="deviceType"
|
||||
[hasPermissionToDeleteItem]="hasPermissionToDeleteWatchlistItem"
|
||||
[locale]="user?.settings?.locale || undefined"
|
||||
[user]="user"
|
||||
(itemDeleted)="onWatchlistItemDeleted($event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -327,6 +327,10 @@ export class DataService {
|
||||
return this.http.delete<any>(`/api/v1/user/${aId}`);
|
||||
}
|
||||
|
||||
public deleteWatchlistItem({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
return this.http.delete<any>(`/api/v1/watchlist/${dataSource}/${symbol}`);
|
||||
}
|
||||
|
||||
public fetchAccesses() {
|
||||
return this.http.get<Access[]>('/api/v1/access');
|
||||
}
|
||||
|
@ -113,6 +113,39 @@
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions" stickyEnd>
|
||||
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell></th>
|
||||
<td *matCellDef="let element" class="px-1 text-center" mat-cell>
|
||||
@if (hasPermissionToDeleteItem) {
|
||||
<button
|
||||
class="mx-1 no-min-width px-2"
|
||||
mat-button
|
||||
[matMenuTriggerFor]="benchmarkMenu"
|
||||
(click)="$event.stopPropagation()"
|
||||
>
|
||||
<ion-icon name="ellipsis-horizontal" />
|
||||
</button>
|
||||
}
|
||||
<mat-menu #benchmarkMenu="matMenu" xPosition="before">
|
||||
<button
|
||||
mat-menu-item
|
||||
[disabled]="!hasPermissionToDeleteItem"
|
||||
(click)="
|
||||
onDeleteItem({
|
||||
dataSource: element.dataSource,
|
||||
symbol: element.symbol
|
||||
})
|
||||
"
|
||||
>
|
||||
<span class="align-items-center d-flex">
|
||||
<ion-icon class="mr-2" name="trash-outline" />
|
||||
<span i18n>Delete</span>
|
||||
</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
|
||||
<tr
|
||||
*matRowDef="let row; columns: displayedColumns"
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { ConfirmationDialogType } from '@ghostfolio/client/core/notification/confirmation-dialog/confirmation-dialog.type';
|
||||
import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
|
||||
import { getLocale, resolveMarketCondition } from '@ghostfolio/common/helper';
|
||||
import {
|
||||
AssetProfileIdentifier,
|
||||
@ -13,11 +15,15 @@ import {
|
||||
CUSTOM_ELEMENTS_SCHEMA,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnDestroy
|
||||
OnDestroy,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { isNumber } from 'lodash';
|
||||
@ -33,6 +39,8 @@ import { BenchmarkDetailDialogParams } from './benchmark-detail-dialog/interface
|
||||
CommonModule,
|
||||
GfTrendIndicatorComponent,
|
||||
GfValueComponent,
|
||||
MatButtonModule,
|
||||
MatMenuModule,
|
||||
MatTableModule,
|
||||
NgxSkeletonLoaderModule,
|
||||
RouterModule
|
||||
@ -45,10 +53,19 @@ import { BenchmarkDetailDialogParams } from './benchmark-detail-dialog/interface
|
||||
export class GfBenchmarkComponent implements OnChanges, OnDestroy {
|
||||
@Input() benchmarks: Benchmark[];
|
||||
@Input() deviceType: string;
|
||||
@Input() hasPermissionToDeleteItem: boolean;
|
||||
@Input() locale = getLocale();
|
||||
@Input() user: User;
|
||||
|
||||
public displayedColumns = ['name', 'date', 'change', 'marketCondition'];
|
||||
@Output() itemDeleted = new EventEmitter<AssetProfileIdentifier>();
|
||||
|
||||
public displayedColumns = [
|
||||
'name',
|
||||
'date',
|
||||
'change',
|
||||
'marketCondition',
|
||||
'actions'
|
||||
];
|
||||
public isLoading = true;
|
||||
public isNumber = isNumber;
|
||||
public resolveMarketCondition = resolveMarketCondition;
|
||||
@ -58,6 +75,7 @@ export class GfBenchmarkComponent implements OnChanges, OnDestroy {
|
||||
|
||||
public constructor(
|
||||
private dialog: MatDialog,
|
||||
private notificationService: NotificationService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router
|
||||
) {
|
||||
@ -89,11 +107,22 @@ export class GfBenchmarkComponent implements OnChanges, OnDestroy {
|
||||
'trend200d',
|
||||
'date',
|
||||
'change',
|
||||
'marketCondition'
|
||||
'marketCondition',
|
||||
'actions'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public onDeleteItem({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.notificationService.confirm({
|
||||
confirmFn: () => {
|
||||
this.itemDeleted.emit({ dataSource, symbol });
|
||||
},
|
||||
confirmType: ConfirmationDialogType.Warn,
|
||||
title: $localize`Do you really want to delete this item?`
|
||||
});
|
||||
}
|
||||
|
||||
public onOpenBenchmarkDialog({ dataSource, symbol }: AssetProfileIdentifier) {
|
||||
this.router.navigate([], {
|
||||
queryParams: { dataSource, symbol, benchmarkDetailDialog: true }
|
||||
|
Loading…
x
Reference in New Issue
Block a user