Feature/extend import validation message (#421)

* Extend import validation message

* Update changelog
pull/422/head
Thomas Kaul 3 years ago committed by GitHub
parent 67dbc6b014
commit b57301ef50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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
- Extended the validation message of the import functionality for transactions
## 1.61.0 - 15.10.2021
### Added

@ -94,7 +94,9 @@ export class ImportService {
]);
if (result[symbol] === undefined) {
throw new Error(`${symbol} is not a valid symbol for ${dataSource}`);
throw new Error(
`orders.${index}.symbol ("${symbol}") is not valid for the specified data source ("${dataSource}")`
);
}
}
}

@ -16,6 +16,8 @@ import { ImportTransactionDialogParams } from './interfaces/interfaces';
templateUrl: 'import-transaction-dialog.html'
})
export class ImportTransactionDialog implements OnDestroy {
public details: any[] = [];
private unsubscribeSubject = new Subject<void>();
public constructor(
@ -23,7 +25,19 @@ export class ImportTransactionDialog implements OnDestroy {
public dialogRef: MatDialogRef<ImportTransactionDialog>
) {}
public ngOnInit() {}
public ngOnInit() {
for (const message of this.data.messages) {
if (message.includes('orders.')) {
let [index] = message.split(' ');
index = index.replace('orders.', '');
[index] = index.split('.');
this.details.push(this.data.orders[index]);
} else {
this.details.push('');
}
}
}
public onCancel(): void {
this.dialogRef.close();

@ -6,14 +6,27 @@
></gf-dialog-header>
<div class="flex-grow-1" mat-dialog-content>
<ul class="list-unstyled">
<li *ngFor="let message of data.messages" class="d-flex">
<div class="align-items-center d-flex px-2">
<ion-icon name="warning-outline"></ion-icon>
</div>
<div>{{ message }}</div>
</li>
</ul>
<mat-accordion displayMode="flat">
<mat-expansion-panel
*ngFor="let message of data.messages; let i = index"
[disabled]="!details[i]"
>
<mat-expansion-panel-header class="pl-1">
<mat-panel-title>
<div class="d-flex">
<div class="align-items-center d-flex mr-2">
<ion-icon name="warning-outline"></ion-icon>
</div>
<div>{{ message }}</div>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<pre
*ngIf="details[i]"
class="m-0"
><code>{{ details[i] | json }}</code></pre>
</mat-expansion-panel>
</mat-accordion>
</div>
<gf-dialog-footer

@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatExpansionModule } from '@angular/material/expansion';
import { GfDialogFooterModule } from '@ghostfolio/client/components/dialog-footer/dialog-footer.module';
import { GfDialogHeaderModule } from '@ghostfolio/client/components/dialog-header/dialog-header.module';
@ -15,7 +16,8 @@ import { ImportTransactionDialog } from './import-transaction-dialog.component';
GfDialogFooterModule,
GfDialogHeaderModule,
MatButtonModule,
MatDialogModule
MatDialogModule,
MatExpansionModule
],
providers: [],
schemas: [CUSTOM_ELEMENTS_SCHEMA]

@ -1,3 +1,12 @@
:host {
display: block;
.mat-expansion-panel {
background: none;
box-shadow: none;
.mat-expansion-panel-header {
color: inherit;
}
}
}

@ -1,4 +1,5 @@
export interface ImportTransactionDialogParams {
deviceType: string;
messages: string[];
orders: any[];
}

@ -197,7 +197,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
this.handleImportSuccess();
} catch (error) {
this.handleImportError(error);
this.handleImportError({ error, orders: content.orders });
}
return;
@ -212,7 +212,10 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
this.handleImportSuccess();
} catch (error) {
this.handleImportError({
error: { message: error?.error?.message ?? [error?.message] }
error: {
error: { message: error?.error?.message ?? [error?.message] }
},
orders: error?.orders ?? []
});
}
@ -221,7 +224,10 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
throw new Error();
} catch (error) {
this.handleImportError({ error: { message: ['Unexpected format'] } });
this.handleImportError({
error: { error: { message: ['Unexpected format'] } },
orders: []
});
}
};
};
@ -307,13 +313,14 @@ export class TransactionsPageComponent implements OnDestroy, OnInit {
a.click();
}
private handleImportError(aError: any) {
private handleImportError({ error, orders }: { error: any; orders: any[] }) {
this.snackBar.dismiss();
this.dialog.open(ImportTransactionDialog, {
data: {
orders,
deviceType: this.deviceType,
messages: aError?.error?.message
messages: error?.error?.message
},
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
});

@ -39,17 +39,17 @@ export class ImportTransactionsService {
const orders: CreateOrderDto[] = [];
for (const item of content) {
for (const [index, item] of content.entries()) {
orders.push({
accountId: defaultAccountId,
currency: this.parseCurrency(item),
currency: this.parseCurrency({ content, index, item }),
dataSource: primaryDataSource,
date: this.parseDate(item),
fee: this.parseFee(item),
quantity: this.parseQuantity(item),
symbol: this.parseSymbol(item),
type: this.parseType(item),
unitPrice: this.parseUnitPrice(item)
date: this.parseDate({ content, index, item }),
fee: this.parseFee({ content, index, item }),
quantity: this.parseQuantity({ content, index, item }),
symbol: this.parseSymbol({ content, index, item }),
type: this.parseType({ content, index, item }),
unitPrice: this.parseUnitPrice({ content, index, item })
});
}
@ -90,8 +90,16 @@ export class ImportTransactionsService {
}, {});
}
private parseCurrency(aItem: any) {
const item = this.lowercaseKeys(aItem);
private parseCurrency({
content,
index,
item
}: {
content: any[];
index: number;
item: any;
}) {
item = this.lowercaseKeys(item);
for (const key of ImportTransactionsService.CURRENCY_KEYS) {
if (item[key]) {
@ -99,11 +107,19 @@ export class ImportTransactionsService {
}
}
throw new Error('Could not parse currency');
throw { message: `orders.${index}.currency is not valid`, orders: content };
}
private parseDate(aItem: any) {
const item = this.lowercaseKeys(aItem);
private parseDate({
content,
index,
item
}: {
content: any[];
index: number;
item: any;
}) {
item = this.lowercaseKeys(item);
let date: string;
for (const key of ImportTransactionsService.DATE_KEYS) {
@ -122,11 +138,19 @@ export class ImportTransactionsService {
}
}
throw new Error('Could not parse date');
throw { message: `orders.${index}.date is not valid`, orders: content };
}
private parseFee(aItem: any) {
const item = this.lowercaseKeys(aItem);
private parseFee({
content,
index,
item
}: {
content: any[];
index: number;
item: any;
}) {
item = this.lowercaseKeys(item);
for (const key of ImportTransactionsService.FEE_KEYS) {
if ((item[key] || item[key] === 0) && isNumber(item[key])) {
@ -134,11 +158,19 @@ export class ImportTransactionsService {
}
}
throw new Error('Could not parse fee');
throw { message: `orders.${index}.fee is not valid`, orders: content };
}
private parseQuantity(aItem: any) {
const item = this.lowercaseKeys(aItem);
private parseQuantity({
content,
index,
item
}: {
content: any[];
index: number;
item: any;
}) {
item = this.lowercaseKeys(item);
for (const key of ImportTransactionsService.QUANTITY_KEYS) {
if (item[key] && isNumber(item[key])) {
@ -146,11 +178,19 @@ export class ImportTransactionsService {
}
}
throw new Error('Could not parse quantity');
throw { message: `orders.${index}.quantity is not valid`, orders: content };
}
private parseSymbol(aItem: any) {
const item = this.lowercaseKeys(aItem);
private parseSymbol({
content,
index,
item
}: {
content: any[];
index: number;
item: any;
}) {
item = this.lowercaseKeys(item);
for (const key of ImportTransactionsService.SYMBOL_KEYS) {
if (item[key]) {
@ -158,11 +198,19 @@ export class ImportTransactionsService {
}
}
throw new Error('Could not parse symbol');
throw { message: `orders.${index}.symbol is not valid`, orders: content };
}
private parseType(aItem: any) {
const item = this.lowercaseKeys(aItem);
private parseType({
content,
index,
item
}: {
content: any[];
index: number;
item: any;
}) {
item = this.lowercaseKeys(item);
for (const key of ImportTransactionsService.TYPE_KEYS) {
if (item[key]) {
@ -174,11 +222,19 @@ export class ImportTransactionsService {
}
}
throw new Error('Could not parse type');
throw { message: `orders.${index}.type is not valid`, orders: content };
}
private parseUnitPrice(aItem: any) {
const item = this.lowercaseKeys(aItem);
private parseUnitPrice({
content,
index,
item
}: {
content: any[];
index: number;
item: any;
}) {
item = this.lowercaseKeys(item);
for (const key of ImportTransactionsService.UNIT_PRICE_KEYS) {
if (item[key] && isNumber(item[key])) {
@ -186,7 +242,10 @@ export class ImportTransactionsService {
}
}
throw new Error('Could not parse unit price (unitPrice)');
throw {
message: `orders.${index}.unitPrice is not valid`,
orders: content
};
}
private postImport(aImportData: { orders: CreateOrderDto[] }) {

@ -1,4 +1,5 @@
import { DataSource } from '@prisma/client';
import { Statistics } from './statistics.interface';
import { Subscription } from './subscription.interface';

Loading…
Cancel
Save