Feature/extend import validation message (#421)
* Extend import validation message * Update changelog
This commit is contained in:
parent
67dbc6b014
commit
b57301ef50
@ -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">
|
||||
<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>
|
||||
</li>
|
||||
</ul>
|
||||
</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: {
|
||||
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…
x
Reference in New Issue
Block a user