Browse Source

Add support to delete a cash balance (#2707)

pull/2709/head
Thomas Kaul 1 year ago
committed by GitHub
parent
commit
377ba75e4c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 51
      apps/api/src/app/account-balance/account-balance.controller.ts
  2. 5
      apps/api/src/app/account-balance/account-balance.module.ts
  3. 19
      apps/api/src/app/account-balance/account-balance.service.ts
  4. 2
      apps/api/src/app/account/account.controller.ts
  5. 2
      apps/api/src/app/account/account.module.ts
  6. 2
      apps/api/src/app/account/account.service.ts
  7. 2
      apps/api/src/app/order/order.module.ts
  8. 2
      apps/api/src/app/portfolio/portfolio.module.ts
  9. 2
      apps/api/src/app/portfolio/portfolio.service.ts
  10. 105
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts
  11. 3
      apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html
  12. 4
      apps/client/src/app/services/data.service.ts
  13. 3
      libs/common/src/lib/permissions.ts
  14. 23
      libs/ui/src/lib/account-balances/account-balances.component.html
  15. 55
      libs/ui/src/lib/account-balances/account-balances.component.ts
  16. 11
      libs/ui/src/lib/account-balances/account-balances.module.ts

51
apps/api/src/app/account-balance/account-balance.controller.ts

@ -0,0 +1,51 @@
import type { RequestWithUser } from '@ghostfolio/common/types';
import {
Controller,
Delete,
HttpException,
Inject,
Param,
UseGuards
} from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { AccountBalanceService } from './account-balance.service';
import { AuthGuard } from '@nestjs/passport';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
import { AccountBalance } from '@prisma/client';
@Controller('account-balance')
export class AccountBalanceController {
public constructor(
private readonly accountBalanceService: AccountBalanceService,
@Inject(REQUEST) private readonly request: RequestWithUser
) {}
@Delete(':id')
@UseGuards(AuthGuard('jwt'))
public async deleteAccountBalance(
@Param('id') id: string
): Promise<AccountBalance> {
const accountBalance = await this.accountBalanceService.accountBalance({
id
});
if (
!hasPermission(
this.request.user.permissions,
permissions.deleteAccountBalance
) ||
!accountBalance ||
accountBalance.userId !== this.request.user.id
) {
throw new HttpException(
getReasonPhrase(StatusCodes.FORBIDDEN),
StatusCodes.FORBIDDEN
);
}
return this.accountBalanceService.deleteAccountBalance({
id
});
}
}

5
apps/api/src/services/account-balance/account-balance.module.ts → apps/api/src/app/account-balance/account-balance.module.ts

@ -1,9 +1,12 @@
import { AccountBalanceService } from '@ghostfolio/api/services/account-balance/account-balance.service';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';
import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { AccountBalanceController } from './account-balance.controller';
import { AccountBalanceService } from './account-balance.service';
@Module({ @Module({
controllers: [AccountBalanceController],
exports: [AccountBalanceService], exports: [AccountBalanceService],
imports: [ExchangeRateDataModule, PrismaModule], imports: [ExchangeRateDataModule, PrismaModule],
providers: [AccountBalanceService] providers: [AccountBalanceService]

19
apps/api/src/services/account-balance/account-balance.service.ts → apps/api/src/app/account-balance/account-balance.service.ts

@ -12,6 +12,17 @@ export class AccountBalanceService {
private readonly prismaService: PrismaService private readonly prismaService: PrismaService
) {} ) {}
public async accountBalance(
accountBalanceWhereInput: Prisma.AccountBalanceWhereInput
): Promise<AccountBalance | null> {
return this.prismaService.accountBalance.findFirst({
include: {
Account: true
},
where: accountBalanceWhereInput
});
}
public async createAccountBalance( public async createAccountBalance(
data: Prisma.AccountBalanceCreateInput data: Prisma.AccountBalanceCreateInput
): Promise<AccountBalance> { ): Promise<AccountBalance> {
@ -20,6 +31,14 @@ export class AccountBalanceService {
}); });
} }
public async deleteAccountBalance(
where: Prisma.AccountBalanceWhereUniqueInput
): Promise<AccountBalance> {
return this.prismaService.accountBalance.delete({
where
});
}
public async getAccountBalances({ public async getAccountBalances({
filters, filters,
user, user,

2
apps/api/src/app/account/account.controller.ts

@ -1,6 +1,6 @@
import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service';
import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service'; import { PortfolioService } from '@ghostfolio/api/app/portfolio/portfolio.service';
import { RedactValuesInResponseInterceptor } from '@ghostfolio/api/interceptors/redact-values-in-response.interceptor'; import { RedactValuesInResponseInterceptor } from '@ghostfolio/api/interceptors/redact-values-in-response.interceptor';
import { AccountBalanceService } from '@ghostfolio/api/services/account-balance/account-balance.service';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service';
import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config'; import { HEADER_KEY_IMPERSONATION } from '@ghostfolio/common/config';
import { import {

2
apps/api/src/app/account/account.module.ts

@ -1,7 +1,7 @@
import { AccountBalanceModule } from '@ghostfolio/api/app/account-balance/account-balance.module';
import { PortfolioModule } from '@ghostfolio/api/app/portfolio/portfolio.module'; import { PortfolioModule } from '@ghostfolio/api/app/portfolio/portfolio.module';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module';
import { AccountBalanceModule } from '@ghostfolio/api/services/account-balance/account-balance.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module';
import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module';

2
apps/api/src/app/account/account.service.ts

@ -1,4 +1,4 @@
import { AccountBalanceService } from '@ghostfolio/api/services/account-balance/account-balance.service'; import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { Filter } from '@ghostfolio/common/interfaces'; import { Filter } from '@ghostfolio/common/interfaces';

2
apps/api/src/app/order/order.module.ts

@ -1,8 +1,8 @@
import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service';
import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { CacheModule } from '@ghostfolio/api/app/cache/cache.module'; import { CacheModule } from '@ghostfolio/api/app/cache/cache.module';
import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module'; import { RedisCacheModule } from '@ghostfolio/api/app/redis-cache/redis-cache.module';
import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module';
import { AccountBalanceService } from '@ghostfolio/api/services/account-balance/account-balance.service';
import { ApiModule } from '@ghostfolio/api/services/api/api.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module'; import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';

2
apps/api/src/app/portfolio/portfolio.module.ts

@ -1,8 +1,8 @@
import { AccessModule } from '@ghostfolio/api/app/access/access.module'; import { AccessModule } from '@ghostfolio/api/app/access/access.module';
import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service';
import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { OrderModule } from '@ghostfolio/api/app/order/order.module'; import { OrderModule } from '@ghostfolio/api/app/order/order.module';
import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { UserModule } from '@ghostfolio/api/app/user/user.module';
import { AccountBalanceService } from '@ghostfolio/api/services/account-balance/account-balance.service';
import { ApiModule } from '@ghostfolio/api/services/api/api.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module';
import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module';
import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module'; import { DataGatheringModule } from '@ghostfolio/api/services/data-gathering/data-gathering.module';

2
apps/api/src/app/portfolio/portfolio.service.ts

@ -1,3 +1,4 @@
import { AccountBalanceService } from '@ghostfolio/api/app/account-balance/account-balance.service';
import { AccountService } from '@ghostfolio/api/app/account/account.service'; import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { CashDetails } from '@ghostfolio/api/app/account/interfaces/cash-details.interface'; import { CashDetails } from '@ghostfolio/api/app/account/interfaces/cash-details.interface';
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface';
@ -12,7 +13,6 @@ import { CurrencyClusterRiskBaseCurrencyCurrentInvestment } from '@ghostfolio/ap
import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment'; import { CurrencyClusterRiskCurrentInvestment } from '@ghostfolio/api/models/rules/currency-cluster-risk/current-investment';
import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup'; import { EmergencyFundSetup } from '@ghostfolio/api/models/rules/emergency-fund/emergency-fund-setup';
import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment'; import { FeeRatioInitialInvestment } from '@ghostfolio/api/models/rules/fees/fee-ratio-initial-investment';
import { AccountBalanceService } from '@ghostfolio/api/services/account-balance/account-balance.service';
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service';
import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service'; import { ImpersonationService } from '@ghostfolio/api/services/impersonation/impersonation.service';

105
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts

@ -11,7 +11,11 @@ import { DataService } from '@ghostfolio/client/services/data.service';
import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service';
import { UserService } from '@ghostfolio/client/services/user/user.service'; import { UserService } from '@ghostfolio/client/services/user/user.service';
import { downloadAsFile } from '@ghostfolio/common/helper'; import { downloadAsFile } from '@ghostfolio/common/helper';
import { HistoricalDataItem, User } from '@ghostfolio/common/interfaces'; import {
AccountBalancesResponse,
HistoricalDataItem,
User
} from '@ghostfolio/common/interfaces';
import { OrderWithAccount } from '@ghostfolio/common/types'; import { OrderWithAccount } from '@ghostfolio/common/types';
import Big from 'big.js'; import Big from 'big.js';
import { format, parseISO } from 'date-fns'; import { format, parseISO } from 'date-fns';
@ -20,6 +24,7 @@ import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { AccountDetailDialogParams } from './interfaces/interfaces'; import { AccountDetailDialogParams } from './interfaces/interfaces';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
@Component({ @Component({
host: { class: 'd-flex flex-column h-100' }, host: { class: 'd-flex flex-column h-100' },
@ -29,11 +34,13 @@ import { AccountDetailDialogParams } from './interfaces/interfaces';
styleUrls: ['./account-detail-dialog.component.scss'] styleUrls: ['./account-detail-dialog.component.scss']
}) })
export class AccountDetailDialog implements OnDestroy, OnInit { export class AccountDetailDialog implements OnDestroy, OnInit {
public accountBalances: AccountBalancesResponse['balances'];
public activities: OrderWithAccount[]; public activities: OrderWithAccount[];
public balance: number; public balance: number;
public currency: string; public currency: string;
public equity: number; public equity: number;
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
public hasPermissionToDeleteAccountBalance: boolean;
public historicalDataItems: HistoricalDataItem[]; public historicalDataItems: HistoricalDataItem[];
public isLoadingActivities: boolean; public isLoadingActivities: boolean;
public isLoadingChart: boolean; public isLoadingChart: boolean;
@ -59,6 +66,11 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
if (state?.user) { if (state?.user) {
this.user = state.user; this.user = state.user;
this.hasPermissionToDeleteAccountBalance = hasPermission(
this.user.permissions,
permissions.deleteAccountBalance
);
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
}); });
@ -66,7 +78,6 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
public ngOnInit() { public ngOnInit() {
this.isLoadingActivities = true; this.isLoadingActivities = true;
this.isLoadingChart = true;
this.dataService this.dataService
.fetchAccount(this.data.accountId) .fetchAccount(this.data.accountId)
@ -112,48 +123,33 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}); });
this.dataService
.fetchPortfolioPerformance({
filters: [
{
id: this.data.accountId,
type: 'ACCOUNT'
}
],
range: 'max',
withExcludedAccounts: true
})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ chart }) => {
this.historicalDataItems = chart.map(
({ date, netWorth, netWorthInPercentage }) => {
return {
date,
value:
this.hasImpersonationId || this.user.settings.isRestrictedView
? netWorthInPercentage
: netWorth
};
}
);
this.isLoadingChart = false;
this.changeDetectorRef.markForCheck();
});
this.impersonationStorageService this.impersonationStorageService
.onChangeHasImpersonation() .onChangeHasImpersonation()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((impersonationId) => { .subscribe((impersonationId) => {
this.hasImpersonationId = !!impersonationId; this.hasImpersonationId = !!impersonationId;
}); });
this.fetchAccountBalances();
this.fetchPortfolioPerformance();
} }
public onClose() { public onClose() {
this.dialogRef.close(); this.dialogRef.close();
} }
public onDeleteAccountBalance(aId: string) {
this.dataService
.deleteAccountBalance(aId)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe({
next: () => {
this.fetchAccountBalances();
this.fetchPortfolioPerformance();
}
});
}
public onExport() { public onExport() {
this.dataService this.dataService
.fetchExport( .fetchExport(
@ -176,6 +172,51 @@ export class AccountDetailDialog implements OnDestroy, OnInit {
}); });
} }
private fetchAccountBalances() {
this.dataService
.fetchAccountBalances(this.data.accountId)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ balances }) => {
this.accountBalances = balances;
this.changeDetectorRef.markForCheck();
});
}
private fetchPortfolioPerformance() {
this.isLoadingChart = true;
this.dataService
.fetchPortfolioPerformance({
filters: [
{
id: this.data.accountId,
type: 'ACCOUNT'
}
],
range: 'max',
withExcludedAccounts: true
})
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ chart }) => {
this.historicalDataItems = chart.map(
({ date, netWorth, netWorthInPercentage }) => {
return {
date,
value:
this.hasImpersonationId || this.user.settings.isRestrictedView
? netWorthInPercentage
: netWorth
};
}
);
this.isLoadingChart = false;
this.changeDetectorRef.markForCheck();
});
}
public ngOnDestroy() { public ngOnDestroy() {
this.unsubscribeSubject.next(); this.unsubscribeSubject.next();
this.unsubscribeSubject.complete(); this.unsubscribeSubject.complete();

3
apps/client/src/app/components/account-detail-dialog/account-detail-dialog.html

@ -87,8 +87,11 @@
<mat-tab> <mat-tab>
<ng-template i18n mat-tab-label>Cash Balances</ng-template> <ng-template i18n mat-tab-label>Cash Balances</ng-template>
<gf-account-balances <gf-account-balances
[accountBalances]="accountBalances"
[accountId]="data.accountId" [accountId]="data.accountId"
[locale]="user?.settings?.locale" [locale]="user?.settings?.locale"
[showActions]="!hasImpersonationId && hasPermissionToDeleteAccountBalance && !user.settings.isRestrictedView"
(accountBalanceDeleted)="onDeleteAccountBalance($event)"
></gf-account-balances> ></gf-account-balances>
</mat-tab> </mat-tab>
</mat-tab-group> </mat-tab-group>

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

@ -212,6 +212,10 @@ export class DataService {
return this.http.delete<any>(`/api/v1/account/${aId}`); return this.http.delete<any>(`/api/v1/account/${aId}`);
} }
public deleteAccountBalance(aId: string) {
return this.http.delete<any>(`/api/v1/account-balance/${aId}`);
}
public deleteAllOrders() { public deleteAllOrders() {
return this.http.delete<any>(`/api/v1/order/`); return this.http.delete<any>(`/api/v1/order/`);
} }

3
libs/common/src/lib/permissions.ts

@ -12,6 +12,7 @@ export const permissions = {
createUserAccount: 'createUserAccount', createUserAccount: 'createUserAccount',
deleteAccess: 'deleteAccess', deleteAccess: 'deleteAccess',
deleteAccount: 'deleteAcccount', deleteAccount: 'deleteAcccount',
deleteAccountBalance: 'deleteAcccountBalance',
deleteAuthDevice: 'deleteAuthDevice', deleteAuthDevice: 'deleteAuthDevice',
deleteOrder: 'deleteOrder', deleteOrder: 'deleteOrder',
deletePlatform: 'deletePlatform', deletePlatform: 'deletePlatform',
@ -45,6 +46,7 @@ export function getPermissions(aRole: Role): string[] {
permissions.accessAssistant, permissions.accessAssistant,
permissions.createAccess, permissions.createAccess,
permissions.createAccount, permissions.createAccount,
permissions.deleteAccountBalance,
permissions.createOrder, permissions.createOrder,
permissions.createPlatform, permissions.createPlatform,
permissions.createTag, permissions.createTag,
@ -75,6 +77,7 @@ export function getPermissions(aRole: Role): string[] {
permissions.createOrder, permissions.createOrder,
permissions.deleteAccess, permissions.deleteAccess,
permissions.deleteAccount, permissions.deleteAccount,
permissions.deleteAccountBalance,
permissions.deleteAuthDevice, permissions.deleteAuthDevice,
permissions.deleteOrder, permissions.deleteOrder,
permissions.updateAccount, permissions.updateAccount,

23
libs/ui/src/lib/account-balances/account-balances.component.html

@ -31,6 +31,29 @@
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="actions">
<th *matHeaderCellDef class="px-1 text-center" mat-header-cell></th>
<td *matCellDef="let element" class="px-1 text-center" mat-cell>
<button
*ngIf="showActions"
class="mx-1 no-min-width px-2"
mat-button
[matMenuTriggerFor]="accountBalanceMenu"
(click)="$event.stopPropagation()"
>
<ion-icon name="ellipsis-horizontal"></ion-icon>
</button>
<mat-menu #accountBalanceMenu="matMenu" xPosition="before">
<button mat-menu-item (click)="onDeleteAccountBalance(element.id)">
<span class="align-items-center d-flex">
<ion-icon class="mr-2" name="trash-outline"></ion-icon>
<span i18n>Delete</span>
</span>
</button>
</mat-menu>
</td>
</ng-container>
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr> <tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
<tr *matRowDef="let row; columns: displayedColumns" mat-row></tr> <tr *matRowDef="let row; columns: displayedColumns" mat-row></tr>
</table> </table>

55
libs/ui/src/lib/account-balances/account-balances.component.ts

@ -1,18 +1,19 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef,
Component, Component,
EventEmitter,
Input, Input,
OnChanges,
OnDestroy, OnDestroy,
OnInit, OnInit,
Output,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { MatSort } from '@angular/material/sort'; import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { DataService } from '@ghostfolio/client/services/data.service';
import { AccountBalancesResponse } from '@ghostfolio/common/interfaces'; import { AccountBalancesResponse } from '@ghostfolio/common/interfaces';
import { get } from 'lodash'; import { get } from 'lodash';
import { Subject, takeUntil } from 'rxjs'; import { Subject } from 'rxjs';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
@ -20,44 +21,48 @@ import { Subject, takeUntil } from 'rxjs';
styleUrls: ['./account-balances.component.scss'], styleUrls: ['./account-balances.component.scss'],
templateUrl: './account-balances.component.html' templateUrl: './account-balances.component.html'
}) })
export class AccountBalancesComponent implements OnDestroy, OnInit { export class AccountBalancesComponent implements OnChanges, OnDestroy, OnInit {
@Input() accountBalances: AccountBalancesResponse['balances'];
@Input() accountId: string; @Input() accountId: string;
@Input() locale: string; @Input() locale: string;
@Input() showActions = true;
@Output() accountBalanceDeleted = new EventEmitter<string>();
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
public dataSource: MatTableDataSource< public dataSource: MatTableDataSource<
AccountBalancesResponse['balances'][0] AccountBalancesResponse['balances'][0]
> = new MatTableDataSource(); > = new MatTableDataSource();
public displayedColumns: string[] = ['date', 'value']; public displayedColumns: string[] = ['date', 'value', 'actions'];
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
public constructor( public constructor() {}
private changeDetectorRef: ChangeDetectorRef,
private dataService: DataService
) {}
public ngOnInit() { public ngOnInit() {}
this.fetchBalances();
}
public ngOnDestroy() { public ngOnChanges() {
this.unsubscribeSubject.next(); if (this.accountBalances) {
this.unsubscribeSubject.complete(); this.dataSource = new MatTableDataSource(this.accountBalances);
this.dataSource.sort = this.sort;
this.dataSource.sortingDataAccessor = get;
}
} }
private fetchBalances() { public onDeleteAccountBalance(aId: string) {
this.dataService const confirmation = confirm(
.fetchAccountBalances(this.accountId) $localize`Do you really want to delete this account balance?`
.pipe(takeUntil(this.unsubscribeSubject)) );
.subscribe(({ balances }) => {
this.dataSource = new MatTableDataSource(balances);
this.dataSource.sort = this.sort; if (confirmation) {
this.dataSource.sortingDataAccessor = get; this.accountBalanceDeleted.emit(aId);
}
}
this.changeDetectorRef.markForCheck(); public ngOnDestroy() {
}); this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
} }
} }

11
libs/ui/src/lib/account-balances/account-balances.module.ts

@ -1,5 +1,7 @@
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 { MatMenuModule } from '@angular/material/menu';
import { MatSortModule } from '@angular/material/sort'; import { MatSortModule } from '@angular/material/sort';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { GfValueModule } from '@ghostfolio/ui/value'; import { GfValueModule } from '@ghostfolio/ui/value';
@ -9,7 +11,14 @@ import { AccountBalancesComponent } from './account-balances.component';
@NgModule({ @NgModule({
declarations: [AccountBalancesComponent], declarations: [AccountBalancesComponent],
exports: [AccountBalancesComponent], exports: [AccountBalancesComponent],
imports: [CommonModule, GfValueModule, MatSortModule, MatTableModule], imports: [
CommonModule,
GfValueModule,
MatButtonModule,
MatMenuModule,
MatSortModule,
MatTableModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA] schemas: [CUSTOM_ELEMENTS_SCHEMA]
}) })
export class GfAccountBalancesModule {} export class GfAccountBalancesModule {}

Loading…
Cancel
Save