diff --git a/apps/api/src/app/account/transfer-balance.dto.ts b/apps/api/src/app/account/transfer-balance.dto.ts
new file mode 100644
index 000000000..fb602033e
--- /dev/null
+++ b/apps/api/src/app/account/transfer-balance.dto.ts
@@ -0,0 +1,12 @@
+import { IsNumber, IsString } from 'class-validator';
+
+export class TransferBalanceDto {
+ @IsString()
+ accountIdFrom: string;
+
+ @IsString()
+ accountIdTo: string;
+
+ @IsNumber()
+ balance: number;
+}
diff --git a/apps/client/src/app/components/accounts-table/accounts-table.component.html b/apps/client/src/app/components/accounts-table/accounts-table.component.html
index d3ece8977..664694735 100644
--- a/apps/client/src/app/components/accounts-table/accounts-table.component.html
+++ b/apps/client/src/app/components/accounts-table/accounts-table.component.html
@@ -1,3 +1,14 @@
+
+
+
+
();
@Output() accountToUpdate = new EventEmitter();
+ @Output() transferBalance = new EventEmitter();
@ViewChild(MatSort) sort: MatSort;
@@ -97,6 +98,10 @@ export class AccountsTableComponent implements OnChanges, OnDestroy, OnInit {
alert(aComment);
}
+ public onTransferBalance() {
+ this.transferBalance.emit();
+ }
+
public onUpdateAccount(aAccount: AccountModel) {
this.accountToUpdate.emit(aAccount);
}
diff --git a/apps/client/src/app/pages/accounts/accounts-page.component.ts b/apps/client/src/app/pages/accounts/accounts-page.component.ts
index ab03340df..18d5d1e2a 100644
--- a/apps/client/src/app/pages/accounts/accounts-page.component.ts
+++ b/apps/client/src/app/pages/accounts/accounts-page.component.ts
@@ -2,6 +2,7 @@ import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
+import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto';
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
import { AccountDetailDialog } from '@ghostfolio/client/components/account-detail-dialog/account-detail-dialog.component';
import { AccountDetailDialogParams } from '@ghostfolio/client/components/account-detail-dialog/interfaces/interfaces';
@@ -16,6 +17,7 @@ import { Subject, Subscription } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { CreateOrUpdateAccountDialog } from './create-or-update-account-dialog/create-or-update-account-dialog.component';
+import { TransferBalanceDialog } from './transfer-balance/transfer-balance-dialog.component';
@Component({
host: { class: 'page' },
@@ -67,6 +69,8 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
} else {
this.router.navigate(['.'], { relativeTo: this.route });
}
+ } else if (params['transferBalanceDialog']) {
+ this.openTransferBalanceDialog();
}
});
}
@@ -144,6 +148,12 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
});
}
+ public onTransferBalance() {
+ this.router.navigate([], {
+ queryParams: { transferBalanceDialog: true }
+ });
+ }
+
public onUpdateAccount(aAccount: AccountModel) {
this.router.navigate([], {
queryParams: { accountId: aAccount.id, editDialog: true }
@@ -267,4 +277,30 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
this.router.navigate(['.'], { relativeTo: this.route });
});
}
+
+ private openTransferBalanceDialog(): void {
+ const dialogRef = this.dialog.open(TransferBalanceDialog, {
+ data: {
+ accounts: this.accounts
+ },
+ height: this.deviceType === 'mobile' ? '97.5vh' : '80vh',
+ width: this.deviceType === 'mobile' ? '100vw' : '50rem'
+ });
+
+ dialogRef
+ .afterClosed()
+ .pipe(takeUntil(this.unsubscribeSubject))
+ .subscribe((data: any) => {
+ if (data) {
+ const { accountIdFrom, accountIdTo, balance }: TransferBalanceDto =
+ data?.account;
+
+ console.log(
+ `Transfer cash balance of ${balance} from account ${accountIdFrom} to account ${accountIdTo}`
+ );
+ }
+
+ this.router.navigate(['.'], { relativeTo: this.route });
+ });
+ }
}
diff --git a/apps/client/src/app/pages/accounts/accounts-page.html b/apps/client/src/app/pages/accounts/accounts-page.html
index 6057802df..a7d5901bb 100644
--- a/apps/client/src/app/pages/accounts/accounts-page.html
+++ b/apps/client/src/app/pages/accounts/accounts-page.html
@@ -14,6 +14,7 @@
[transactionCount]="transactionCount"
(accountDeleted)="onDeleteAccount($event)"
(accountToUpdate)="onUpdateAccount($event)"
+ (transferBalance)="onTransferBalance()"
>
diff --git a/apps/client/src/app/pages/accounts/accounts-page.module.ts b/apps/client/src/app/pages/accounts/accounts-page.module.ts
index 9edb43ba7..7de50983c 100644
--- a/apps/client/src/app/pages/accounts/accounts-page.module.ts
+++ b/apps/client/src/app/pages/accounts/accounts-page.module.ts
@@ -8,6 +8,7 @@ import { GfAccountsTableModule } from '@ghostfolio/client/components/accounts-ta
import { AccountsPageRoutingModule } from './accounts-page-routing.module';
import { AccountsPageComponent } from './accounts-page.component';
import { GfCreateOrUpdateAccountDialogModule } from './create-or-update-account-dialog/create-or-update-account-dialog.module';
+import { GfTransferBalanceDialogModule } from './transfer-balance/transfer-balance-dialog.module';
@NgModule({
declarations: [AccountsPageComponent],
@@ -17,6 +18,7 @@ import { GfCreateOrUpdateAccountDialogModule } from './create-or-update-account-
GfAccountDetailDialogModule,
GfAccountsTableModule,
GfCreateOrUpdateAccountDialogModule,
+ GfTransferBalanceDialogModule,
MatButtonModule,
RouterModule
],
diff --git a/apps/client/src/app/pages/accounts/transfer-balance/interfaces/interfaces.ts b/apps/client/src/app/pages/accounts/transfer-balance/interfaces/interfaces.ts
new file mode 100644
index 000000000..3a0b921fd
--- /dev/null
+++ b/apps/client/src/app/pages/accounts/transfer-balance/interfaces/interfaces.ts
@@ -0,0 +1,5 @@
+import { Account } from '@prisma/client';
+
+export interface TransferBalanceDialogParams {
+ accounts: Account[];
+}
diff --git a/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts
new file mode 100644
index 000000000..4850e96e3
--- /dev/null
+++ b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts
@@ -0,0 +1,69 @@
+import {
+ ChangeDetectionStrategy,
+ Component,
+ Inject,
+ OnDestroy
+} from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { TransferBalanceDto } from '@ghostfolio/api/app/account/transfer-balance.dto';
+import { Account } from '@prisma/client';
+import { Subject } from 'rxjs';
+
+import { TransferBalanceDialogParams } from './interfaces/interfaces';
+
+@Component({
+ host: { class: 'h-100' },
+ selector: 'gf-transfer-balance-dialog',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ styleUrls: ['./transfer-balance-dialog.scss'],
+ templateUrl: 'transfer-balance-dialog.html'
+})
+export class TransferBalanceDialog implements OnDestroy {
+ public accounts: Account[] = [];
+ public currency: string;
+ public transferBalanceForm: FormGroup;
+
+ private unsubscribeSubject = new Subject();
+
+ public constructor(
+ @Inject(MAT_DIALOG_DATA) public data: TransferBalanceDialogParams,
+ public dialogRef: MatDialogRef,
+ private formBuilder: FormBuilder
+ ) {}
+
+ public ngOnInit() {
+ this.accounts = this.data.accounts;
+
+ this.transferBalanceForm = this.formBuilder.group({
+ balance: [0, Validators.required],
+ fromAccount: ['', Validators.required],
+ toAccount: ['', Validators.required]
+ });
+
+ this.transferBalanceForm.get('fromAccount').valueChanges.subscribe((id) => {
+ this.currency = this.accounts.find((account) => {
+ return account.id === id;
+ }).currency;
+ });
+ }
+
+ public onCancel() {
+ this.dialogRef.close();
+ }
+
+ public onSubmit() {
+ const account: TransferBalanceDto = {
+ accountIdFrom: this.transferBalanceForm.controls['fromAccount'].value,
+ accountIdTo: this.transferBalanceForm.controls['toAccount'].value,
+ balance: this.transferBalanceForm.controls['balance'].value
+ };
+
+ this.dialogRef.close({ account });
+ }
+
+ public ngOnDestroy() {
+ this.unsubscribeSubject.next();
+ this.unsubscribeSubject.complete();
+ }
+}
diff --git a/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.html b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.html
new file mode 100644
index 000000000..9cce7b87a
--- /dev/null
+++ b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.html
@@ -0,0 +1,53 @@
+
diff --git a/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.module.ts b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.module.ts
new file mode 100644
index 000000000..5a56b5810
--- /dev/null
+++ b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.module.ts
@@ -0,0 +1,24 @@
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { ReactiveFormsModule } from '@angular/forms';
+import { MatButtonModule } from '@angular/material/button';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
+import { MatSelectModule } from '@angular/material/select';
+
+import { TransferBalanceDialog } from './transfer-balance-dialog.component';
+
+@NgModule({
+ declarations: [TransferBalanceDialog],
+ imports: [
+ CommonModule,
+ MatButtonModule,
+ MatDialogModule,
+ MatFormFieldModule,
+ MatInputModule,
+ MatSelectModule,
+ ReactiveFormsModule
+ ]
+})
+export class GfTransferBalanceDialogModule {}
diff --git a/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.scss b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.scss
new file mode 100644
index 000000000..b63df0134
--- /dev/null
+++ b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.scss
@@ -0,0 +1,7 @@
+:host {
+ display: block;
+
+ .mat-mdc-dialog-content {
+ max-height: unset;
+ }
+}
|