Feature/validate forms using DTO for access, asset profile, tag and platform management (#3337)
* Validate forms using DTO for access, asset profile, tag and platform management * Update changelog
This commit is contained in:
parent
4efd5cefd8
commit
2173c418a7
@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added a form validation against the DTO in the create or update access dialog
|
||||||
|
- Added a form validation against the DTO in the asset profile details dialog of the admin control
|
||||||
|
- Added a form validation against the DTO in the platform management of the admin control panel
|
||||||
|
- Added a form validation against the DTO in the tag management of the admin control panel
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed an issue in the calculation of the portfolio summary caused by future liabilities
|
- Fixed an issue in the calculation of the portfolio summary caused by future liabilities
|
||||||
|
@ -3,6 +3,7 @@ import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-dat
|
|||||||
import { AdminMarketDataService } from '@ghostfolio/client/components/admin-market-data/admin-market-data.service';
|
import { AdminMarketDataService } from '@ghostfolio/client/components/admin-market-data/admin-market-data.service';
|
||||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
|
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
|
||||||
import { ghostfolioScraperApiSymbolPrefix } from '@ghostfolio/common/config';
|
import { ghostfolioScraperApiSymbolPrefix } from '@ghostfolio/common/config';
|
||||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||||
import {
|
import {
|
||||||
@ -258,7 +259,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSubmit() {
|
public async onSubmit() {
|
||||||
let countries = [];
|
let countries = [];
|
||||||
let scraperConfiguration = {};
|
let scraperConfiguration = {};
|
||||||
let sectors = [];
|
let sectors = [];
|
||||||
@ -299,6 +300,17 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
|||||||
url: this.assetProfileForm.get('url').value || null
|
url: this.assetProfileForm.get('url').value || null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await validateObjectForForm({
|
||||||
|
classDto: UpdateAssetProfileDto,
|
||||||
|
form: this.assetProfileForm,
|
||||||
|
object: assetProfileData
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.adminService
|
this.adminService
|
||||||
.patchAssetProfile({
|
.patchAssetProfile({
|
||||||
...assetProfileData,
|
...assetProfileData,
|
||||||
|
@ -143,9 +143,7 @@ export class AdminPlatformComponent implements OnInit, OnDestroy {
|
|||||||
dialogRef
|
dialogRef
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((data) => {
|
.subscribe((platform: CreatePlatformDto | null) => {
|
||||||
const platform: CreatePlatformDto = data?.platform;
|
|
||||||
|
|
||||||
if (platform) {
|
if (platform) {
|
||||||
this.adminService
|
this.adminService
|
||||||
.postPlatform(platform)
|
.postPlatform(platform)
|
||||||
@ -182,9 +180,7 @@ export class AdminPlatformComponent implements OnInit, OnDestroy {
|
|||||||
dialogRef
|
dialogRef
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((data) => {
|
.subscribe((platform: UpdatePlatformDto | null) => {
|
||||||
const platform: UpdatePlatformDto = data?.platform;
|
|
||||||
|
|
||||||
if (platform) {
|
if (platform) {
|
||||||
this.adminService
|
this.adminService
|
||||||
.putPlatform(platform)
|
.putPlatform(platform)
|
||||||
|
@ -1,4 +1,14 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
import { CreatePlatformDto } from '@ghostfolio/api/app/platform/create-platform.dto';
|
||||||
|
import { UpdatePlatformDto } from '@ghostfolio/api/app/platform/update-platform.dto';
|
||||||
|
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
|
||||||
|
|
||||||
|
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 { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
@ -11,18 +21,54 @@ import { CreateOrUpdatePlatformDialogParams } from './interfaces/interfaces';
|
|||||||
styleUrls: ['./create-or-update-platform-dialog.scss'],
|
styleUrls: ['./create-or-update-platform-dialog.scss'],
|
||||||
templateUrl: 'create-or-update-platform-dialog.html'
|
templateUrl: 'create-or-update-platform-dialog.html'
|
||||||
})
|
})
|
||||||
export class CreateOrUpdatePlatformDialog {
|
export class CreateOrUpdatePlatformDialog implements OnDestroy {
|
||||||
|
public platformForm: FormGroup;
|
||||||
|
|
||||||
private unsubscribeSubject = new Subject<void>();
|
private unsubscribeSubject = new Subject<void>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdatePlatformDialogParams,
|
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdatePlatformDialogParams,
|
||||||
public dialogRef: MatDialogRef<CreateOrUpdatePlatformDialog>
|
public dialogRef: MatDialogRef<CreateOrUpdatePlatformDialog>,
|
||||||
) {}
|
private formBuilder: FormBuilder
|
||||||
|
) {
|
||||||
|
this.platformForm = this.formBuilder.group({
|
||||||
|
name: [this.data.platform.name, Validators.required],
|
||||||
|
url: [this.data.platform.url, Validators.required]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public onCancel() {
|
public onCancel() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async onSubmit() {
|
||||||
|
try {
|
||||||
|
const platform: CreatePlatformDto | UpdatePlatformDto = {
|
||||||
|
name: this.platformForm.get('name')?.value,
|
||||||
|
url: this.platformForm.get('url')?.value
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.data.platform.id) {
|
||||||
|
(platform as UpdatePlatformDto).id = this.data.platform.id;
|
||||||
|
await validateObjectForForm({
|
||||||
|
classDto: UpdatePlatformDto,
|
||||||
|
form: this.platformForm,
|
||||||
|
object: platform
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await validateObjectForForm({
|
||||||
|
classDto: CreatePlatformDto,
|
||||||
|
form: this.platformForm,
|
||||||
|
object: platform
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialogRef.close(platform);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
this.unsubscribeSubject.next();
|
this.unsubscribeSubject.next();
|
||||||
this.unsubscribeSubject.complete();
|
this.unsubscribeSubject.complete();
|
||||||
|
@ -1,17 +1,30 @@
|
|||||||
<form #addPlatformForm="ngForm" class="d-flex flex-column h-100">
|
<form
|
||||||
|
class="d-flex flex-column h-100"
|
||||||
|
[formGroup]="platformForm"
|
||||||
|
(keyup.enter)="platformForm.valid && onSubmit()"
|
||||||
|
(ngSubmit)="onSubmit()"
|
||||||
|
>
|
||||||
<h1 *ngIf="data.platform.id" i18n mat-dialog-title>Update platform</h1>
|
<h1 *ngIf="data.platform.id" i18n mat-dialog-title>Update platform</h1>
|
||||||
<h1 *ngIf="!data.platform.id" i18n mat-dialog-title>Add platform</h1>
|
<h1 *ngIf="!data.platform.id" i18n mat-dialog-title>Add platform</h1>
|
||||||
<div class="flex-grow-1 py-3" mat-dialog-content>
|
<div class="flex-grow-1 py-3" mat-dialog-content>
|
||||||
<div>
|
<div>
|
||||||
<mat-form-field appearance="outline" class="w-100">
|
<mat-form-field appearance="outline" class="w-100">
|
||||||
<mat-label i18n>Name</mat-label>
|
<mat-label i18n>Name</mat-label>
|
||||||
<input matInput name="name" required [(ngModel)]="data.platform.name" />
|
<input
|
||||||
|
formControlName="name"
|
||||||
|
matInput
|
||||||
|
(keydown.enter)="$event.stopPropagation()"
|
||||||
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-form-field appearance="outline" class="w-100">
|
<mat-form-field appearance="outline" class="w-100">
|
||||||
<mat-label i18n>Url</mat-label>
|
<mat-label i18n>Url</mat-label>
|
||||||
<input matInput name="url" required [(ngModel)]="data.platform.url" />
|
<input
|
||||||
|
formControlName="url"
|
||||||
|
matInput
|
||||||
|
(keydown.enter)="$event.stopPropagation()"
|
||||||
|
/>
|
||||||
@if (data.platform.url) {
|
@if (data.platform.url) {
|
||||||
<gf-asset-profile-icon
|
<gf-asset-profile-icon
|
||||||
class="mr-3"
|
class="mr-3"
|
||||||
@ -23,12 +36,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="justify-content-end" mat-dialog-actions>
|
<div class="justify-content-end" mat-dialog-actions>
|
||||||
<button i18n mat-button (click)="onCancel()">Cancel</button>
|
<button i18n mat-button type="button" (click)="onCancel()">Cancel</button>
|
||||||
<button
|
<button
|
||||||
color="primary"
|
color="primary"
|
||||||
mat-flat-button
|
mat-flat-button
|
||||||
[disabled]="!addPlatformForm.form.valid"
|
type="submit"
|
||||||
[mat-dialog-close]="data"
|
[disabled]="!platformForm.valid"
|
||||||
>
|
>
|
||||||
<ng-container i18n>Save</ng-container>
|
<ng-container i18n>Save</ng-container>
|
||||||
</button>
|
</button>
|
||||||
|
@ -142,9 +142,7 @@ export class AdminTagComponent implements OnInit, OnDestroy {
|
|||||||
dialogRef
|
dialogRef
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((data) => {
|
.subscribe((tag: CreateTagDto | null) => {
|
||||||
const tag: CreateTagDto = data?.tag;
|
|
||||||
|
|
||||||
if (tag) {
|
if (tag) {
|
||||||
this.adminService
|
this.adminService
|
||||||
.postTag(tag)
|
.postTag(tag)
|
||||||
@ -180,9 +178,7 @@ export class AdminTagComponent implements OnInit, OnDestroy {
|
|||||||
dialogRef
|
dialogRef
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((data) => {
|
.subscribe((tag: UpdateTagDto | null) => {
|
||||||
const tag: UpdateTagDto = data?.tag;
|
|
||||||
|
|
||||||
if (tag) {
|
if (tag) {
|
||||||
this.adminService
|
this.adminService
|
||||||
.putTag(tag)
|
.putTag(tag)
|
||||||
|
@ -1,4 +1,14 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
import { CreateTagDto } from '@ghostfolio/api/app/tag/create-tag.dto';
|
||||||
|
import { UpdateTagDto } from '@ghostfolio/api/app/tag/update-tag.dto';
|
||||||
|
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
Inject,
|
||||||
|
OnDestroy
|
||||||
|
} from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
@ -11,18 +21,52 @@ import { CreateOrUpdateTagDialogParams } from './interfaces/interfaces';
|
|||||||
styleUrls: ['./create-or-update-tag-dialog.scss'],
|
styleUrls: ['./create-or-update-tag-dialog.scss'],
|
||||||
templateUrl: 'create-or-update-tag-dialog.html'
|
templateUrl: 'create-or-update-tag-dialog.html'
|
||||||
})
|
})
|
||||||
export class CreateOrUpdateTagDialog {
|
export class CreateOrUpdateTagDialog implements OnDestroy {
|
||||||
|
public tagForm: FormGroup;
|
||||||
|
|
||||||
private unsubscribeSubject = new Subject<void>();
|
private unsubscribeSubject = new Subject<void>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateTagDialogParams,
|
@Inject(MAT_DIALOG_DATA) public data: CreateOrUpdateTagDialogParams,
|
||||||
public dialogRef: MatDialogRef<CreateOrUpdateTagDialog>
|
public dialogRef: MatDialogRef<CreateOrUpdateTagDialog>,
|
||||||
) {}
|
private formBuilder: FormBuilder
|
||||||
|
) {
|
||||||
|
this.tagForm = this.formBuilder.group({
|
||||||
|
name: [this.data.tag.name]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public onCancel() {
|
public onCancel() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async onSubmit() {
|
||||||
|
try {
|
||||||
|
const tag: CreateTagDto | UpdateTagDto = {
|
||||||
|
name: this.tagForm.get('name')?.value
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.data.tag.id) {
|
||||||
|
(tag as UpdateTagDto).id = this.data.tag.id;
|
||||||
|
await validateObjectForForm({
|
||||||
|
classDto: UpdateTagDto,
|
||||||
|
form: this.tagForm,
|
||||||
|
object: tag
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await validateObjectForForm({
|
||||||
|
classDto: CreateTagDto,
|
||||||
|
form: this.tagForm,
|
||||||
|
object: tag
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialogRef.close(tag);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
this.unsubscribeSubject.next();
|
this.unsubscribeSubject.next();
|
||||||
this.unsubscribeSubject.complete();
|
this.unsubscribeSubject.complete();
|
||||||
|
@ -1,21 +1,30 @@
|
|||||||
<form #addTagForm="ngForm" class="d-flex flex-column h-100">
|
<form
|
||||||
|
class="d-flex flex-column h-100"
|
||||||
|
[formGroup]="tagForm"
|
||||||
|
(keyup.enter)="tagForm.valid && onSubmit()"
|
||||||
|
(ngSubmit)="onSubmit()"
|
||||||
|
>
|
||||||
<h1 *ngIf="data.tag.id" i18n mat-dialog-title>Update tag</h1>
|
<h1 *ngIf="data.tag.id" i18n mat-dialog-title>Update tag</h1>
|
||||||
<h1 *ngIf="!data.tag.id" i18n mat-dialog-title>Add tag</h1>
|
<h1 *ngIf="!data.tag.id" i18n mat-dialog-title>Add tag</h1>
|
||||||
<div class="flex-grow-1 py-3" mat-dialog-content>
|
<div class="flex-grow-1 py-3" mat-dialog-content>
|
||||||
<div>
|
<div>
|
||||||
<mat-form-field appearance="outline" class="w-100">
|
<mat-form-field appearance="outline" class="w-100">
|
||||||
<mat-label i18n>Name</mat-label>
|
<mat-label i18n>Name</mat-label>
|
||||||
<input matInput name="name" required [(ngModel)]="data.tag.name" />
|
<input
|
||||||
|
formControlName="name"
|
||||||
|
matInput
|
||||||
|
(keydown.enter)="$event.stopPropagation()"
|
||||||
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="justify-content-end" mat-dialog-actions>
|
<div class="justify-content-end" mat-dialog-actions>
|
||||||
<button i18n mat-button (click)="onCancel()">Cancel</button>
|
<button i18n mat-button type="button" (click)="onCancel()">Cancel</button>
|
||||||
<button
|
<button
|
||||||
color="primary"
|
color="primary"
|
||||||
mat-flat-button
|
mat-flat-button
|
||||||
[disabled]="!addTagForm.form.valid"
|
type="submit"
|
||||||
[mat-dialog-close]="data"
|
[disabled]="!tagForm.valid"
|
||||||
>
|
>
|
||||||
<ng-container i18n>Save</ng-container>
|
<ng-container i18n>Save</ng-container>
|
||||||
</button>
|
</button>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto';
|
import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
|
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
@ -40,22 +41,22 @@ export class CreateOrUpdateAccessDialog implements OnDestroy {
|
|||||||
alias: [this.data.access.alias],
|
alias: [this.data.access.alias],
|
||||||
permissions: [this.data.access.permissions[0], Validators.required],
|
permissions: [this.data.access.permissions[0], Validators.required],
|
||||||
type: [this.data.access.type, Validators.required],
|
type: [this.data.access.type, Validators.required],
|
||||||
userId: [this.data.access.grantee, Validators.required]
|
granteeUserId: [this.data.access.grantee, Validators.required]
|
||||||
});
|
});
|
||||||
|
|
||||||
this.accessForm.get('type').valueChanges.subscribe((accessType) => {
|
this.accessForm.get('type').valueChanges.subscribe((accessType) => {
|
||||||
|
const granteeUserIdControl = this.accessForm.get('granteeUserId');
|
||||||
const permissionsControl = this.accessForm.get('permissions');
|
const permissionsControl = this.accessForm.get('permissions');
|
||||||
const userIdControl = this.accessForm.get('userId');
|
|
||||||
|
|
||||||
if (accessType === 'PRIVATE') {
|
if (accessType === 'PRIVATE') {
|
||||||
|
granteeUserIdControl.setValidators(Validators.required);
|
||||||
permissionsControl.setValidators(Validators.required);
|
permissionsControl.setValidators(Validators.required);
|
||||||
userIdControl.setValidators(Validators.required);
|
|
||||||
} else {
|
} else {
|
||||||
userIdControl.clearValidators();
|
granteeUserIdControl.clearValidators();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
granteeUserIdControl.updateValueAndValidity();
|
||||||
permissionsControl.updateValueAndValidity();
|
permissionsControl.updateValueAndValidity();
|
||||||
userIdControl.updateValueAndValidity();
|
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
@ -65,28 +66,38 @@ export class CreateOrUpdateAccessDialog implements OnDestroy {
|
|||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSubmit() {
|
public async onSubmit() {
|
||||||
const access: CreateAccessDto = {
|
const access: CreateAccessDto = {
|
||||||
alias: this.accessForm.get('alias').value,
|
alias: this.accessForm.get('alias').value,
|
||||||
granteeUserId: this.accessForm.get('userId').value,
|
granteeUserId: this.accessForm.get('granteeUserId').value,
|
||||||
permissions: [this.accessForm.get('permissions').value]
|
permissions: [this.accessForm.get('permissions').value]
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dataService
|
try {
|
||||||
.postAccess(access)
|
await validateObjectForForm({
|
||||||
.pipe(
|
classDto: CreateAccessDto,
|
||||||
catchError((error) => {
|
form: this.accessForm,
|
||||||
if (error.status === StatusCodes.BAD_REQUEST) {
|
object: access
|
||||||
alert($localize`Oops! Could not grant access.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return EMPTY;
|
|
||||||
}),
|
|
||||||
takeUntil(this.unsubscribeSubject)
|
|
||||||
)
|
|
||||||
.subscribe(() => {
|
|
||||||
this.dialogRef.close({ access });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.dataService
|
||||||
|
.postAccess(access)
|
||||||
|
.pipe(
|
||||||
|
catchError((error) => {
|
||||||
|
if (error.status === StatusCodes.BAD_REQUEST) {
|
||||||
|
alert($localize`Oops! Could not grant access.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EMPTY;
|
||||||
|
}),
|
||||||
|
takeUntil(this.unsubscribeSubject)
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.dialogRef.close(access);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
Ghostfolio <ng-container i18n>User ID</ng-container>
|
Ghostfolio <ng-container i18n>User ID</ng-container>
|
||||||
</mat-label>
|
</mat-label>
|
||||||
<input
|
<input
|
||||||
formControlName="userId"
|
formControlName="granteeUserId"
|
||||||
matInput
|
matInput
|
||||||
type="text"
|
type="text"
|
||||||
(keydown.enter)="$event.stopPropagation()"
|
(keydown.enter)="$event.stopPropagation()"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { CreateAccessDto } from '@ghostfolio/api/app/access/create-access.dto';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||||
import { Access, User } from '@ghostfolio/common/interfaces';
|
import { Access, User } from '@ghostfolio/common/interfaces';
|
||||||
@ -113,7 +114,7 @@ export class UserAccountAccessComponent implements OnDestroy, OnInit {
|
|||||||
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
width: this.deviceType === 'mobile' ? '100vw' : '50rem'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((access) => {
|
dialogRef.afterClosed().subscribe((access: CreateAccessDto | null) => {
|
||||||
if (access) {
|
if (access) {
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
@ -189,9 +189,7 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
|
|||||||
dialogRef
|
dialogRef
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((data: any) => {
|
.subscribe((account: UpdateAccountDto | null) => {
|
||||||
const account: UpdateAccountDto = data?.account;
|
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
this.dataService
|
this.dataService
|
||||||
.putAccount(account)
|
.putAccount(account)
|
||||||
@ -258,9 +256,7 @@ export class AccountsPageComponent implements OnDestroy, OnInit {
|
|||||||
dialogRef
|
dialogRef
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((data: any) => {
|
.subscribe((account: CreateAccountDto | null) => {
|
||||||
const account: CreateAccountDto = data?.account;
|
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
this.dataService
|
this.dataService
|
||||||
.postAccount(account)
|
.postAccount(account)
|
||||||
|
@ -123,6 +123,8 @@ export class CreateOrUpdateAccountDialog implements OnDestroy {
|
|||||||
form: this.accountForm,
|
form: this.accountForm,
|
||||||
object: account
|
object: account
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.dialogRef.close(account as UpdateAccountDto);
|
||||||
} else {
|
} else {
|
||||||
delete (account as CreateAccountDto).id;
|
delete (account as CreateAccountDto).id;
|
||||||
|
|
||||||
@ -131,9 +133,9 @@ export class CreateOrUpdateAccountDialog implements OnDestroy {
|
|||||||
form: this.accountForm,
|
form: this.accountForm,
|
||||||
object: account
|
object: account
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
this.dialogRef.close({ account });
|
this.dialogRef.close(account as CreateAccountDto);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
@ -287,9 +287,7 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit {
|
|||||||
dialogRef
|
dialogRef
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((data: any) => {
|
.subscribe((transaction: UpdateOrderDto | null) => {
|
||||||
const transaction: UpdateOrderDto = data?.activity;
|
|
||||||
|
|
||||||
if (transaction) {
|
if (transaction) {
|
||||||
this.dataService
|
this.dataService
|
||||||
.putOrder(transaction)
|
.putOrder(transaction)
|
||||||
@ -338,9 +336,7 @@ export class ActivitiesPageComponent implements OnDestroy, OnInit {
|
|||||||
dialogRef
|
dialogRef
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((data: any) => {
|
.subscribe((transaction: CreateOrderDto | null) => {
|
||||||
const transaction: CreateOrderDto = data?.activity;
|
|
||||||
|
|
||||||
if (transaction) {
|
if (transaction) {
|
||||||
this.dataService.postOrder(transaction).subscribe({
|
this.dataService.postOrder(transaction).subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
|
@ -475,6 +475,8 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
ignoreFields: ['dataSource', 'date'],
|
ignoreFields: ['dataSource', 'date'],
|
||||||
object: activity as UpdateOrderDto
|
object: activity as UpdateOrderDto
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.dialogRef.close(activity as UpdateOrderDto);
|
||||||
} else {
|
} else {
|
||||||
(activity as CreateOrderDto).updateAccountBalance =
|
(activity as CreateOrderDto).updateAccountBalance =
|
||||||
this.activityForm.get('updateAccountBalance').value;
|
this.activityForm.get('updateAccountBalance').value;
|
||||||
@ -485,9 +487,9 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
ignoreFields: ['dataSource', 'date'],
|
ignoreFields: ['dataSource', 'date'],
|
||||||
object: activity
|
object: activity
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
this.dialogRef.close({ activity });
|
this.dialogRef.close(activity as CreateOrderDto);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,14 @@ export async function validateObjectForForm<T>({
|
|||||||
validationError: Object.values(constraints)[0]
|
validationError: Object.values(constraints)[0]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formControlInCustomCurrency = form.get(`${property}InCustomCurrency`);
|
||||||
|
|
||||||
|
if (formControlInCustomCurrency) {
|
||||||
|
formControlInCustomCurrency.setErrors({
|
||||||
|
validationError: Object.values(constraints)[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject(nonIgnoredErrors);
|
return Promise.reject(nonIgnoredErrors);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user