Merge branch 'main' of github.com:ghostfolio/ghostfolio
All checks were successful
Docker image CD / build_and_push (push) Successful in 34m50s
All checks were successful
Docker image CD / build_and_push (push) Successful in 34m50s
This commit is contained in:
commit
ea05e61bd4
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Reloaded the available tags after creating a custom tag in the holding detail dialog (experimental)
|
- Reloaded the available tags after creating a custom tag in the holding detail dialog (experimental)
|
||||||
|
- Improved the validation of the currency management in the admin control panel
|
||||||
- Migrated the `@ghostfolio/client` components to control flow
|
- Migrated the `@ghostfolio/client` components to control flow
|
||||||
- Migrated the `@ghostfolio/ui` components to control flow
|
- Migrated the `@ghostfolio/ui` components to control flow
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { AdminService } from '@ghostfolio/api/app/admin/admin.service';
|
import { AdminService } from '@ghostfolio/api/app/admin/admin.service';
|
||||||
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
|
||||||
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
|
||||||
|
import { getCurrencyFromSymbol, isCurrency } from '@ghostfolio/common/helper';
|
||||||
import { MarketDataDetailsResponse } from '@ghostfolio/common/interfaces';
|
import { MarketDataDetailsResponse } from '@ghostfolio/common/interfaces';
|
||||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||||
import { RequestWithUser } from '@ghostfolio/common/types';
|
import { RequestWithUser } from '@ghostfolio/common/types';
|
||||||
@ -42,7 +43,7 @@ export class MarketDataController {
|
|||||||
{ dataSource, symbol }
|
{ dataSource, symbol }
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!assetProfile) {
|
if (!assetProfile && !isCurrency(getCurrencyFromSymbol(symbol))) {
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
getReasonPhrase(StatusCodes.NOT_FOUND),
|
getReasonPhrase(StatusCodes.NOT_FOUND),
|
||||||
StatusCodes.NOT_FOUND
|
StatusCodes.NOT_FOUND
|
||||||
@ -55,7 +56,7 @@ export class MarketDataController {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const canReadOwnAssetProfile =
|
const canReadOwnAssetProfile =
|
||||||
assetProfile.userId === this.request.user.id &&
|
assetProfile?.userId === this.request.user.id &&
|
||||||
hasPermission(
|
hasPermission(
|
||||||
this.request.user.permissions,
|
this.request.user.permissions,
|
||||||
permissions.readMarketDataOfOwnAssetProfile
|
permissions.readMarketDataOfOwnAssetProfile
|
||||||
|
@ -15,9 +15,11 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
ValidationErrors,
|
ValidationErrors,
|
||||||
|
ValidatorFn,
|
||||||
Validators
|
Validators
|
||||||
} from '@angular/forms';
|
} from '@angular/forms';
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { isISO4217CurrencyCode } from 'class-validator';
|
||||||
import { uniq } from 'lodash';
|
import { uniq } from 'lodash';
|
||||||
import { Subject, takeUntil } from 'rxjs';
|
import { Subject, takeUntil } from 'rxjs';
|
||||||
|
|
||||||
@ -52,9 +54,7 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy {
|
|||||||
this.createAssetProfileForm = this.formBuilder.group(
|
this.createAssetProfileForm = this.formBuilder.group(
|
||||||
{
|
{
|
||||||
addCurrency: new FormControl(null, [
|
addCurrency: new FormControl(null, [
|
||||||
Validators.maxLength(3),
|
this.iso4217CurrencyCodeValidator()
|
||||||
Validators.minLength(3),
|
|
||||||
Validators.required
|
|
||||||
]),
|
]),
|
||||||
addSymbol: new FormControl(null, [Validators.required]),
|
addSymbol: new FormControl(null, [Validators.required]),
|
||||||
searchSymbol: new FormControl(null, [Validators.required])
|
searchSymbol: new FormControl(null, [Validators.required])
|
||||||
@ -83,11 +83,11 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy {
|
|||||||
symbol: this.createAssetProfileForm.get('searchSymbol').value.symbol
|
symbol: this.createAssetProfileForm.get('searchSymbol').value.symbol
|
||||||
});
|
});
|
||||||
} else if (this.mode === 'currency') {
|
} else if (this.mode === 'currency') {
|
||||||
const currency = this.createAssetProfileForm
|
const currency = (
|
||||||
.get('addCurrency')
|
this.createAssetProfileForm.get('addCurrency').value as string
|
||||||
.value.toUpperCase();
|
).toUpperCase();
|
||||||
|
|
||||||
const currencies = uniq([...this.customCurrencies, currency]);
|
const currencies = uniq([...this.customCurrencies, currency]).sort();
|
||||||
|
|
||||||
this.dataService
|
this.dataService
|
||||||
.putAdminSetting(PROPERTY_CURRENCIES, {
|
.putAdminSetting(PROPERTY_CURRENCIES, {
|
||||||
@ -109,10 +109,7 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy {
|
|||||||
const addCurrencyFormControl =
|
const addCurrencyFormControl =
|
||||||
this.createAssetProfileForm.get('addCurrency');
|
this.createAssetProfileForm.get('addCurrency');
|
||||||
|
|
||||||
if (
|
if (addCurrencyFormControl.hasError('invalidCurrency')) {
|
||||||
addCurrencyFormControl.hasError('maxlength') ||
|
|
||||||
addCurrencyFormControl.hasError('minlength')
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,4 +158,14 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy {
|
|||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private iso4217CurrencyCodeValidator(): ValidatorFn {
|
||||||
|
return (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
if (!isISO4217CurrencyCode(control.value?.toUpperCase())) {
|
||||||
|
return { invalidCurrency: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ import {
|
|||||||
formatDistanceToNowStrict,
|
formatDistanceToNowStrict,
|
||||||
parseISO
|
parseISO
|
||||||
} from 'date-fns';
|
} from 'date-fns';
|
||||||
import { uniq } from 'lodash';
|
|
||||||
import { StringValue } from 'ms';
|
import { StringValue } from 'ms';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
@ -122,24 +121,6 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
|
|||||||
this.putAdminSetting({ key: PROPERTY_COUPONS, value: coupons });
|
this.putAdminSetting({ key: PROPERTY_COUPONS, value: coupons });
|
||||||
}
|
}
|
||||||
|
|
||||||
public onAddCurrency() {
|
|
||||||
const currency = prompt($localize`Please add a currency:`);
|
|
||||||
|
|
||||||
if (currency) {
|
|
||||||
if (currency.length === 3) {
|
|
||||||
const currencies = uniq([
|
|
||||||
...this.customCurrencies,
|
|
||||||
currency.toUpperCase()
|
|
||||||
]);
|
|
||||||
this.putAdminSetting({ key: PROPERTY_CURRENCIES, value: currencies });
|
|
||||||
} else {
|
|
||||||
this.notificationService.alert({
|
|
||||||
title: $localize`${currency} is an invalid currency!`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public onChangeCouponDuration(aCouponDuration: StringValue) {
|
public onChangeCouponDuration(aCouponDuration: StringValue) {
|
||||||
this.couponDuration = aCouponDuration;
|
this.couponDuration = aCouponDuration;
|
||||||
}
|
}
|
||||||
|
@ -95,16 +95,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</table>
|
</table>
|
||||||
<div class="mt-2">
|
|
||||||
<button
|
|
||||||
color="primary"
|
|
||||||
mat-flat-button
|
|
||||||
(click)="onAddCurrency()"
|
|
||||||
>
|
|
||||||
<ion-icon class="mr-1" name="add-outline" />
|
|
||||||
<span i18n>Add Currency</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex my-3">
|
<div class="d-flex my-3">
|
||||||
|
@ -56,6 +56,11 @@
|
|||||||
<li>Click on the <i>+</i> button</li>
|
<li>Click on the <i>+</i> button</li>
|
||||||
<li>Switch to <i>Add Currency</i></li>
|
<li>Switch to <i>Add Currency</i></li>
|
||||||
<li>Insert e.g. <code>EUR</code> for Euro</li>
|
<li>Insert e.g. <code>EUR</code> for Euro</li>
|
||||||
|
<li>Select <i>Filter by Currencies</i></li>
|
||||||
|
<li>Find the entry <i>USDEUR</i></li>
|
||||||
|
<li>
|
||||||
|
Click the menu item <i>Gather Historical Data</i> in the dialog
|
||||||
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user