Compare commits

..

7 Commits

Author SHA1 Message Date
0439a7beaa Release 0.92.0 (#48) 2021-04-25 21:27:13 +02:00
608b195ba9 Extend user table (#47)
* Column: accounts
* Horizontal scrollbar
2021-04-25 21:25:23 +02:00
8cb5fd64dd Prepare for multi accounts support: store account for new transactions (#46) 2021-04-25 21:22:35 +02:00
ef317a86ed Setup angular compiler options (#45) 2021-04-25 17:41:53 +02:00
19ada83d0b Fix header and about page (#44) 2021-04-25 17:29:04 +02:00
6ecf66ea2a Fix types 2021-04-25 12:34:34 +02:00
65ba16c07c Fix tests 2021-04-25 12:30:28 +02:00
28 changed files with 264 additions and 133 deletions

View File

@ -5,6 +5,18 @@ 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).
## 0.92.0 - 25.04.2021
### Added
- Prepared further for multi accounts support: store account for new transactions
- Added a horizontal scrollbar to the user table of the admin control panel
### Fixed
- Fixed an issue in the header with outdated data
- Fixed an issue on the about page with outdated data
## 0.91.0 - 25.04.2021
### Added

View File

@ -97,7 +97,7 @@ export class AdminService {
},
select: {
_count: {
select: { Order: true }
select: { Account: true, Order: true }
},
alias: true,
Analytics: {

View File

@ -36,6 +36,8 @@ export class ExperimentalService {
const ordersWithPlatform: OrderWithPlatform[] = aOrders.map((order) => {
return {
...order,
accountId: undefined,
accountUserId: undefined,
createdAt: new Date(),
date: parseISO(order.date),
fee: 0,

View File

@ -1,3 +1,6 @@
import { Settings, User } from '@prisma/client';
import { Account, Settings, User } from '@prisma/client';
export type UserWithSettings = User & { Settings: Settings };
export type UserWithSettings = User & {
Account: Account[];
Settings: Settings;
};

View File

@ -2,6 +2,9 @@ import { Currency, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString, ValidateIf } from 'class-validator';
export class CreateOrderDto {
@IsString()
accountId: string;
@IsString()
currency: Currency;

View File

@ -118,6 +118,9 @@ export class OrderController {
const date = parseISO(data.date);
const accountId = data.accountId;
delete data.accountId;
if (data.platformId) {
const platformId = data.platformId;
delete data.platformId;
@ -126,6 +129,11 @@ export class OrderController {
{
...data,
date,
Account: {
connect: {
id_userId: { id: accountId, userId: this.request.user.id }
}
},
Platform: { connect: { id: platformId } },
User: { connect: { id: this.request.user.id } }
},
@ -138,6 +146,11 @@ export class OrderController {
{
...data,
date,
Account: {
connect: {
id_userId: { id: accountId, userId: this.request.user.id }
}
},
User: { connect: { id: this.request.user.id } }
},
this.request.user.id
@ -169,6 +182,9 @@ export class OrderController {
const date = parseISO(data.date);
const accountId = data.accountId;
delete data.accountId;
if (data.platformId) {
const platformId = data.platformId;
delete data.platformId;
@ -178,6 +194,11 @@ export class OrderController {
data: {
...data,
date,
Account: {
connect: {
id_userId: { id: accountId, userId: this.request.user.id }
}
},
Platform: { connect: { id: platformId } },
User: { connect: { id: this.request.user.id } }
},
@ -199,6 +220,11 @@ export class OrderController {
data: {
...data,
date,
Account: {
connect: {
id_userId: { id: accountId, userId: this.request.user.id }
}
},
Platform: originalOrder.platformId
? { disconnect: true }
: undefined,

View File

@ -2,6 +2,9 @@ import { Currency, Type } from '@prisma/client';
import { IsISO8601, IsNumber, IsString, ValidateIf } from 'class-validator';
export class UpdateOrderDto {
@IsString()
accountId: string;
@IsString()
currency: Currency;

View File

@ -1,9 +1,10 @@
import { Currency } from '@prisma/client';
import { Account, Currency } from '@prisma/client';
import { Access } from './access.interface';
export interface User {
access: Access[];
accounts: Account[];
alias?: string;
id: string;
permissions: string[];

View File

@ -25,6 +25,7 @@ export class UserService {
) {}
public async getUser({
Account,
alias,
id,
role,
@ -53,6 +54,7 @@ export class UserService {
id: accessItem.id
};
}),
accounts: Account,
permissions: currentPermissions,
settings: {
baseCurrency: Settings?.currency || UserService.DEFAULT_CURRENCY,
@ -69,7 +71,7 @@ export class UserService {
userWhereUniqueInput: Prisma.UserWhereUniqueInput
): Promise<UserWithSettings | null> {
const user = await this.prisma.user.findUnique({
include: { Settings: true },
include: { Account: true, Settings: true },
where: userWhereUniqueInput
});

View File

@ -14,6 +14,9 @@ import { PrismaService } from '../services/prisma.service';
import { RulesService } from '../services/rules.service';
import { Portfolio } from './portfolio';
const DEFAULT_ACCOUNT_ID = '693a834b-eb89-42c9-ae47-35196c25d269';
const USER_ID = 'ca6ce867-5d31-495a-bce9-5942bbca9237';
describe('Portfolio', () => {
let alphaVantageService: AlphaVantageService;
let configurationService: ConfigurationService;
@ -69,13 +72,13 @@ describe('Portfolio', () => {
accessToken: null,
alias: 'Test',
createdAt: new Date(),
id: '',
id: USER_ID,
provider: null,
role: Role.USER,
Settings: {
currency: Currency.CHF,
updatedAt: new Date(),
userId: ''
userId: USER_ID
},
thirdPartyId: null,
updatedAt: new Date()
@ -126,6 +129,8 @@ describe('Portfolio', () => {
it('should return ["BTC"]', async () => {
await portfolio.setOrders([
{
accountId: DEFAULT_ACCOUNT_ID,
accountUserId: USER_ID,
createdAt: null,
currency: Currency.USD,
fee: 0,
@ -137,7 +142,7 @@ describe('Portfolio', () => {
type: Type.BUY,
unitPrice: 49631.24,
updatedAt: null,
userId: null
userId: USER_ID
}
]);
@ -224,6 +229,8 @@ describe('Portfolio', () => {
it('should return ["ETHUSD"]', async () => {
await portfolio.setOrders([
{
accountId: DEFAULT_ACCOUNT_ID,
accountUserId: USER_ID,
createdAt: null,
currency: Currency.USD,
fee: 0,
@ -235,7 +242,7 @@ describe('Portfolio', () => {
type: Type.BUY,
unitPrice: 991.49,
updatedAt: null,
userId: null
userId: USER_ID
}
]);
@ -276,7 +283,7 @@ describe('Portfolio', () => {
}
},
quantity: 0.2,
shareCurrent: 1,
// shareCurrent: 1,
shareInvestment: 1,
symbol: 'ETHUSD',
type: 'Cryptocurrency'
@ -316,6 +323,8 @@ describe('Portfolio', () => {
it('should return ["ETHUSD"]', async () => {
await portfolio.setOrders([
{
accountId: DEFAULT_ACCOUNT_ID,
accountUserId: USER_ID,
createdAt: null,
currency: Currency.USD,
fee: 0,
@ -327,9 +336,11 @@ describe('Portfolio', () => {
type: Type.BUY,
unitPrice: 991.49,
updatedAt: null,
userId: null
userId: USER_ID
},
{
accountId: DEFAULT_ACCOUNT_ID,
accountUserId: USER_ID,
createdAt: null,
currency: Currency.USD,
fee: 0,
@ -341,7 +352,7 @@ describe('Portfolio', () => {
type: Type.BUY,
unitPrice: 1050,
updatedAt: null,
userId: null
userId: USER_ID
}
]);
@ -388,6 +399,8 @@ describe('Portfolio', () => {
it('should return ["BTCUSD", "ETHUSD"]', async () => {
await portfolio.setOrders([
{
accountId: DEFAULT_ACCOUNT_ID,
accountUserId: USER_ID,
createdAt: null,
currency: Currency.EUR,
date: new Date(getUtc('2017-08-16')),
@ -399,9 +412,11 @@ describe('Portfolio', () => {
type: Type.BUY,
unitPrice: 3562.089535970158,
updatedAt: null,
userId: null
userId: USER_ID
},
{
accountId: DEFAULT_ACCOUNT_ID,
accountUserId: USER_ID,
createdAt: null,
currency: Currency.USD,
fee: 2.99,
@ -413,7 +428,7 @@ describe('Portfolio', () => {
type: Type.BUY,
unitPrice: 991.49,
updatedAt: null,
userId: null
userId: USER_ID
}
]);
@ -473,6 +488,8 @@ describe('Portfolio', () => {
it('should work with buy and sell', async () => {
await portfolio.setOrders([
{
accountId: DEFAULT_ACCOUNT_ID,
accountUserId: USER_ID,
createdAt: null,
currency: Currency.USD,
fee: 1.0,
@ -484,9 +501,11 @@ describe('Portfolio', () => {
type: Type.BUY,
unitPrice: 991.49,
updatedAt: null,
userId: null
userId: USER_ID
},
{
accountId: DEFAULT_ACCOUNT_ID,
accountUserId: USER_ID,
createdAt: null,
currency: Currency.USD,
fee: 1.0,
@ -498,9 +517,11 @@ describe('Portfolio', () => {
type: Type.SELL,
unitPrice: 1050,
updatedAt: null,
userId: null
userId: USER_ID
},
{
accountId: DEFAULT_ACCOUNT_ID,
accountUserId: USER_ID,
createdAt: null,
currency: Currency.USD,
fee: 1.0,
@ -512,7 +533,7 @@ describe('Portfolio', () => {
type: Type.BUY,
unitPrice: 1050,
updatedAt: null,
userId: null
userId: USER_ID
}
]);

View File

@ -150,7 +150,7 @@
>Account</a
>
<a
*ngIf="hasPermissionForAdminControl"
*ngIf="hasPermissionToAccessAdminControl"
class="d-block d-sm-none"
[routerLink]="['/admin']"
i18n

View File

@ -34,16 +34,16 @@
<div *ngIf="showDetails" class="row">
<div class="d-flex col justify-content-end">
<gf-value
colorizeSign="true"
isCurrency="true"
[colorizeSign]="true"
[isCurrency]="true"
[locale]="locale"
[value]="isLoading ? undefined : performance?.currentNetPerformance"
></gf-value>
</div>
<div class="col">
<gf-value
colorizeSign="true"
isPercent="true"
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
[value]="
isLoading ? undefined : performance?.currentNetPerformancePercent

View File

@ -16,17 +16,17 @@
<div class="d-flex flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end mb-2"
colorizeSign="true"
position="end"
[colorizeSign]="true"
[currency]="baseCurrency"
[locale]="locale"
[value]="isLoading ? undefined : performance?.currentGrossPerformance"
></gf-value>
<gf-value
class="justify-content-end"
colorizeSign="true"
isPercent="true"
position="end"
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
[value]="
isLoading ? undefined : performance?.currentGrossPerformancePercent
@ -39,17 +39,17 @@
<div class="d-flex flex-column flex-wrap justify-content-end">
<gf-value
class="justify-content-end mb-2"
colorizeSign="true"
position="end"
[colorizeSign]="true"
[currency]="baseCurrency"
[locale]="locale"
[value]="isLoading ? undefined : performance?.currentNetPerformance"
></gf-value>
<gf-value
class="justify-content-end"
colorizeSign="true"
isPercent="true"
position="end"
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
[value]="
isLoading ? undefined : performance?.currentNetPerformancePercent

View File

@ -21,10 +21,10 @@
<div class="row">
<div class="col-6 mb-3">
<gf-value
colorizeSign="true"
isPercent="true"
label="Performance"
size="medium"
[colorizeSign]="true"
[isPercent]="true"
[locale]="data.locale"
[value]="grossPerformancePercent"
></gf-value>
@ -76,9 +76,9 @@
</div>
<div class="col-6 mb-3">
<gf-value
isCurrency="true"
label="Quantity"
size="medium"
[isCurrency]="true"
[value]="quantity"
></gf-value>
</div>

View File

@ -42,14 +42,14 @@
<div class="d-flex mt-1">
<gf-value
class="mr-3"
colorizeSign="true"
[colorizeSign]="true"
[currency]="position?.currency"
[locale]="locale"
[value]="position?.grossPerformance"
></gf-value>
<gf-value
colorizeSign="true"
isPercent="true"
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
[value]="position?.grossPerformancePercent"
></gf-value>

View File

@ -28,8 +28,8 @@
<td class="d-none d-lg-table-cell" mat-cell *matCellDef="let element">
<div class="d-flex justify-content-end">
<gf-value
colorizeSign="true"
isPercent="true"
[colorizeSign]="true"
[isPercent]="true"
[locale]="locale"
[value]="isLoading ? undefined : element.grossPerformancePercent"
></gf-value>
@ -50,7 +50,7 @@
<td mat-cell *matCellDef="let element">
<div class="d-flex justify-content-end">
<gf-value
isPercent="true"
[isPercent]="true"
[locale]="locale"
[value]="isLoading ? undefined : element.shareInvestment"
></gf-value>
@ -71,7 +71,7 @@
<td mat-cell *matCellDef="let element">
<div class="d-flex justify-content-end">
<gf-value
isPercent="true"
[isPercent]="true"
[locale]="locale"
[value]="isLoading ? undefined : element.shareCurrent"
></gf-value>

View File

@ -116,7 +116,7 @@
<td *matCellDef="let element" class="d-none d-lg-table-cell" mat-cell>
<div class="d-flex justify-content-end">
<gf-value
isCurrency="true"
[isCurrency]="true"
[locale]="locale"
[value]="isLoading ? undefined : element.quantity"
></gf-value>
@ -137,7 +137,7 @@
<td *matCellDef="let element" class="d-none d-lg-table-cell" mat-cell>
<div class="d-flex justify-content-end">
<gf-value
isCurrency="true"
[isCurrency]="true"
[locale]="locale"
[value]="isLoading ? undefined : element.unitPrice"
></gf-value>
@ -158,7 +158,7 @@
<td class="d-none d-lg-table-cell" mat-cell *matCellDef="let element">
<div class="d-flex justify-content-end">
<gf-value
isCurrency="true"
[isCurrency]="true"
[locale]="locale"
[value]="isLoading ? undefined : element.fee"
></gf-value>

View File

@ -42,6 +42,8 @@ export class AboutPageComponent implements OnInit {
info.globalPermissions,
permissions.enableSubscription
);
this.cd.markForCheck();
});
this.isLoggedIn = !!this.tokenStorageService.getToken();

View File

@ -70,42 +70,36 @@
<div class="col">
<h3 class="mb-3 text-center" i18n>Users</h3>
<mat-card class="px-0">
<mat-card-content>
<table class="users w-100">
<mat-card-content class="users">
<table>
<thead>
<tr class="mat-header-row">
<th class="mat-header-cell pl-2 py-2 text-truncate" i18n>
User
</th>
<th class="mat-header-cell pr-2 py-2 text-truncate" i18n>
<th class="mat-header-cell pl-2 py-2" i18n>User</th>
<th class="mat-header-cell pr-2 py-2" i18n>
Registration Date
</th>
<th class="mat-header-cell pr-2 py-2 text-truncate" i18n>
Transactions
</th>
<th class="mat-header-cell pr-2 py-2 text-truncate" i18n>
Engagement
</th>
<th class="mat-header-cell pr-2 py-2 text-truncate" i18n>
Last Activitiy
</th>
<th class="mat-header-cell pr-2 py-2" i18n>Accounts</th>
<th class="mat-header-cell pr-2 py-2" i18n>Transactions</th>
<th class="mat-header-cell pr-2 py-2" i18n>Engagement</th>
<th class="mat-header-cell pr-2 py-2" i18n>Last Activitiy</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let userItem of users" class="mat-row">
<td class="mat-cell pl-2 py-2 text-truncate">
<td class="mat-cell pl-2 py-2">
{{ userItem.alias || userItem.id }}
</td>
<td class="mat-cell pr-2 py-2 text-truncate">
<td class="mat-cell pr-2 py-2">
{{ userItem.createdAt | date: defaultDateFormat }}
</td>
<td class="mat-cell pr-2 py-2 text-truncate">
{{ userItem._count?.Order }}
<td class="mat-cell pr-2 py-2">
{{ userItem._count?.Account }}
</td>
<td class="mat-cell pr-2 py-2 text-truncate">
<td class="mat-cell pr-2 py-2">{{ userItem._count?.Order }}</td>
<td class="mat-cell pr-2 py-2">
{{ userItem.Analytics?.activityCount }}
</td>
<td class="mat-cell pr-2 py-2 text-truncate">
<td class="mat-cell pr-2 py-2">
{{ formatDistanceToNow(userItem.Analytics?.updatedAt) }}
</td>
</tr>

View File

@ -2,16 +2,23 @@
color: rgb(var(--dark-primary-text));
display: block;
table {
.mat-card-content {
&.users {
table-layout: fixed;
overflow-x: auto;
tr {
&:nth-child(even) {
background-color: rgba(
var(--dark-primary-text),
var(--palette-background-hover-alpha)
);
table {
tr {
&:nth-child(even) {
background-color: rgba(
var(--dark-primary-text),
var(--palette-background-hover-alpha)
);
}
}
.mat-row,
.mat-header-row {
width: 100%;
}
}
}

View File

@ -8,7 +8,7 @@ import { FormControl, Validators } from '@angular/forms';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { Currency, Order as OrderModel } from '@prisma/client';
import { Currency } from '@prisma/client';
import { Observable, Subject } from 'rxjs';
import {
debounceTime,
@ -19,6 +19,7 @@ import {
} from 'rxjs/operators';
import { DataService } from '../../../services/data.service';
import { CreateOrUpdateTransactionDialogParams } from './interfaces/interfaces';
@Component({
host: { class: 'h-100' },
@ -33,7 +34,7 @@ export class CreateOrUpdateTransactionDialog {
public isLoading = false;
public platforms: { id: string; name: string }[];
public searchSymbolCtrl = new FormControl(
this.data.symbol,
this.data.transaction.symbol,
Validators.required
);
@ -43,7 +44,7 @@ export class CreateOrUpdateTransactionDialog {
private cd: ChangeDetectorRef,
private dataService: DataService,
public dialogRef: MatDialogRef<CreateOrUpdateTransactionDialog>,
@Inject(MAT_DIALOG_DATA) public data: OrderModel
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateTransactionDialogParams
) {}
ngOnInit() {
@ -72,14 +73,14 @@ export class CreateOrUpdateTransactionDialog {
public onUpdateSymbol(event: MatAutocompleteSelectedEvent) {
this.isLoading = true;
this.data.symbol = event.option.value;
this.data.transaction.symbol = event.option.value;
this.dataService
.fetchSymbolItem(this.data.symbol)
.fetchSymbolItem(this.data.transaction.symbol)
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ currency, marketPrice }) => {
this.data.currency = currency;
this.data.unitPrice = marketPrice;
this.data.transaction.currency = currency;
this.data.transaction.unitPrice = marketPrice;
this.isLoading = false;
@ -88,10 +89,10 @@ export class CreateOrUpdateTransactionDialog {
}
public onUpdateSymbolByTyping(value: string) {
this.data.currency = null;
this.data.unitPrice = null;
this.data.transaction.currency = null;
this.data.transaction.unitPrice = null;
this.data.symbol = value;
this.data.transaction.symbol = value;
}
public ngOnDestroy() {

View File

@ -1,6 +1,6 @@
<form #addTransactionForm="ngForm" class="d-flex flex-column h-100">
<h1 *ngIf="data.id" mat-dialog-title i18n>Update transaction</h1>
<h1 *ngIf="!data.id" mat-dialog-title i18n>Add transaction</h1>
<h1 *ngIf="data.transaction.id" mat-dialog-title i18n>Update transaction</h1>
<h1 *ngIf="!data.transaction.id" mat-dialog-title i18n>Add transaction</h1>
<div class="flex-grow-1" mat-dialog-content>
<div>
<mat-form-field appearance="outline" class="w-100">
@ -36,7 +36,7 @@
<div>
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Type</mat-label>
<mat-select name="type" required [(value)]="data.type">
<mat-select name="type" required [(value)]="data.transaction.type">
<mat-option value="BUY" i18n> BUY </mat-option>
<mat-option value="SELL" i18n> SELL </mat-option>
</mat-select>
@ -50,7 +50,7 @@
disabled
name="currency"
required
[(value)]="data.currency"
[(value)]="data.transaction.currency"
>
<mat-option *ngFor="let currency of currencies" [value]="currency"
>{{ currency }}</mat-option
@ -67,7 +67,7 @@
name="date"
required
[matDatepicker]="date"
[(ngModel)]="data.date"
[(ngModel)]="data.transaction.date"
/>
<mat-datepicker-toggle matSuffix [for]="date"></mat-datepicker-toggle>
<mat-datepicker #date disabled="false"></mat-datepicker>
@ -81,7 +81,7 @@
name="fee"
required
type="number"
[(ngModel)]="data.fee"
[(ngModel)]="data.transaction.fee"
/>
</mat-form-field>
</div>
@ -93,7 +93,7 @@
name="quantity"
required
type="number"
[(ngModel)]="data.quantity"
[(ngModel)]="data.transaction.quantity"
/>
</mat-form-field>
</div>
@ -105,14 +105,28 @@
name="unitPrice"
required
type="number"
[(ngModel)]="data.unitPrice"
[(ngModel)]="data.transaction.unitPrice"
/>
</mat-form-field>
</div>
<div class="d-none">
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Account</mat-label>
<mat-select
name="accountId"
required
[(value)]="data.transaction.accountId"
>
<mat-option *ngFor="let account of data.accounts" [value]="account.id"
>{{ account.name }}</mat-option
>
</mat-select>
</mat-form-field>
</div>
<div>
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Platform</mat-label>
<mat-select name="platformId" [(value)]="data.platformId">
<mat-select name="platformId" [(value)]="data.transaction.platformId">
<mat-option [value]="null"></mat-option>
<mat-option *ngFor="let platform of platforms" [value]="platform.id"
>{{ platform.name }}</mat-option
@ -127,7 +141,7 @@
color="primary"
i18n
mat-flat-button
[disabled]="!(addTransactionForm.form.valid && data.symbol)"
[disabled]="!(addTransactionForm.form.valid && data.transaction.symbol)"
[mat-dialog-close]="data"
>
Save

View File

@ -0,0 +1,8 @@
import { Order } from '../../interfaces/order.interface';
import { Account } from '@prisma/client';
export interface CreateOrUpdateTransactionDialogParams {
accountId: string;
accounts: Account[];
transaction: Order;
}

View File

@ -1,8 +1,11 @@
export interface Order {
accountId: string;
currency: string;
date: Date;
fee: number;
id: string;
quantity: number;
platformId: string;
symbol: string;
type: string;
unitPrice: number;

View File

@ -122,36 +122,8 @@ export class TransactionsPageComponent implements OnInit {
});
}
private openCreateTransactionDialog(): void {
const dialogRef = this.dialog.open(CreateOrUpdateTransactionDialog, {
data: {
currency: null,
date: new Date(),
fee: 0,
platformId: null,
quantity: null,
symbol: null,
type: 'BUY',
unitPrice: null
},
height: this.deviceType === 'mobile' ? '97.5vh' : '80vh',
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe((order: UpdateOrderDto) => {
if (order) {
this.dataService.postOrder(order).subscribe({
next: () => {
this.fetchOrders();
}
});
}
this.router.navigate(['.'], { relativeTo: this.route });
});
}
public openUpdateTransactionDialog({
accountId,
currency,
date,
fee,
@ -164,23 +136,29 @@ export class TransactionsPageComponent implements OnInit {
}: OrderModel): void {
const dialogRef = this.dialog.open(CreateOrUpdateTransactionDialog, {
data: {
currency,
date,
fee,
id,
platformId,
quantity,
symbol,
type,
unitPrice
accounts: this.user.accounts,
transaction: {
accountId,
currency,
date,
fee,
id,
platformId,
quantity,
symbol,
type,
unitPrice
}
},
height: this.deviceType === 'mobile' ? '97.5vh' : '80vh',
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe((order: UpdateOrderDto) => {
if (order) {
this.dataService.putOrder(order).subscribe({
dialogRef.afterClosed().subscribe((data: any) => {
const transaction: UpdateOrderDto = data?.transaction;
if (transaction) {
this.dataService.putOrder(transaction).subscribe({
next: () => {
this.fetchOrders();
}
@ -195,4 +173,41 @@ export class TransactionsPageComponent implements OnInit {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
private openCreateTransactionDialog(): void {
const dialogRef = this.dialog.open(CreateOrUpdateTransactionDialog, {
data: {
accounts: this.user.accounts,
transaction: {
accountId: this.user.accounts.find((account) => {
return account.isDefault;
})?.id,
currency: null,
date: new Date(),
fee: 0,
platformId: null,
quantity: null,
symbol: null,
type: 'BUY',
unitPrice: null
}
},
height: this.deviceType === 'mobile' ? '97.5vh' : '80vh',
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});
dialogRef.afterClosed().subscribe((data: any) => {
const transaction: UpdateOrderDto = data?.transaction;
if (transaction) {
this.dataService.postOrder(transaction).subscribe({
next: () => {
this.fetchOrders();
}
});
}
this.router.navigate(['.'], { relativeTo: this.route });
});
}
}

View File

@ -12,5 +12,9 @@
{
"path": "./tsconfig.editor.json"
}
]
],
"angularCompilerOptions": {
"strictInjectionParameters": true,
"strictTemplates": false
}
}

View File

@ -1,6 +1,6 @@
{
"name": "ghostfolio",
"version": "0.91.0",
"version": "0.92.0",
"homepage": "https://ghostfol.io",
"license": "AGPL-3.0",
"scripts": {

View File

@ -82,6 +82,16 @@ async function main() {
create: {
accessToken:
'c689bcc894e4a420cb609ee34271f3e07f200594f7d199c50d75add7102889eb60061a04cd2792ebc853c54e37308271271e7bf588657c9e0c37faacbc28c3c6',
Account: {
create: [
{
accountType: AccountType.SECURITIES,
id: 'f4425b66-9ba9-4ac4-93d7-fdf9a145e8cb',
isDefault: true,
name: 'Default Account'
}
]
},
alias: 'Admin',
id: '4e1af723-95f6-44f8-92a7-464df17f6ec3',
role: Role.ADMIN