Browse Source

Merge branch 'main' into compute-snapshot-perf-1

pull/3827/head
Thomas Kaul 11 months ago
committed by GitHub
parent
commit
9f123eac41
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 95
      apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts
  3. 13
      apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html
  4. 2
      apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/interfaces/interfaces.ts
  5. 5
      apps/client/src/app/components/admin-overview/admin-overview.component.ts
  6. 6
      apps/client/src/app/pages/faq/self-hosting/self-hosting-page.html
  7. 64
      package-lock.json
  8. 4
      package.json

2
CHANGELOG.md

@ -10,7 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Optimized the portfolio calculations with smarter cloning of activities
- Integrated the add currency functionality into the market data section of the admin control panel
- Improved the language localization for German (`de`)
- Upgraded `prisma` from version `5.19.1` to `5.20.0`
- Upgraded `webpack-bundle-analyzer` from version `4.10.1` to `4.10.2`
## 2.110.0 - 2024-09-24

95
apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.component.ts

@ -1,7 +1,10 @@
import { AdminService } from '@ghostfolio/client/services/admin.service';
import { DataService } from '@ghostfolio/client/services/data.service';
import { PROPERTY_CURRENCIES } from '@ghostfolio/common/config';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit
@ -15,6 +18,10 @@ import {
Validators
} from '@angular/forms';
import { MatDialogRef } from '@angular/material/dialog';
import { uniq } from 'lodash';
import { Subject, takeUntil } from 'rxjs';
import { CreateAssetProfileDialogMode } from './interfaces/interfaces';
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
@ -25,17 +32,29 @@ import { MatDialogRef } from '@angular/material/dialog';
})
export class CreateAssetProfileDialog implements OnInit, OnDestroy {
public createAssetProfileForm: FormGroup;
public mode: 'auto' | 'manual';
public mode: CreateAssetProfileDialogMode;
private customCurrencies: string[];
private unsubscribeSubject = new Subject<void>();
public constructor(
public readonly adminService: AdminService,
private readonly changeDetectorRef: ChangeDetectorRef,
private readonly dataService: DataService,
public readonly dialogRef: MatDialogRef<CreateAssetProfileDialog>,
public readonly formBuilder: FormBuilder
) {}
public ngOnInit() {
this.initializeCustomCurrencies();
this.createAssetProfileForm = this.formBuilder.group(
{
addCurrency: new FormControl(null, [
Validators.maxLength(3),
Validators.minLength(3),
Validators.required
]),
addSymbol: new FormControl(null, [Validators.required]),
searchSymbol: new FormControl(null, [Validators.required])
},
@ -51,34 +70,75 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy {
this.dialogRef.close();
}
public onRadioChange(mode: 'auto' | 'manual') {
public onRadioChange(mode: CreateAssetProfileDialogMode) {
this.mode = mode;
}
public onSubmit() {
this.mode === 'auto'
? this.dialogRef.close({
dataSource:
this.createAssetProfileForm.get('searchSymbol').value.dataSource,
symbol: this.createAssetProfileForm.get('searchSymbol').value.symbol
if (this.mode === 'auto') {
this.dialogRef.close({
dataSource:
this.createAssetProfileForm.get('searchSymbol').value.dataSource,
symbol: this.createAssetProfileForm.get('searchSymbol').value.symbol
});
} else if (this.mode === 'currency') {
const currency = this.createAssetProfileForm
.get('addCurrency')
.value.toUpperCase();
const currencies = uniq([...this.customCurrencies, currency]);
this.dataService
.putAdminSetting(PROPERTY_CURRENCIES, {
value: JSON.stringify(currencies)
})
: this.dialogRef.close({
dataSource: 'MANUAL',
symbol: this.createAssetProfileForm.get('addSymbol').value
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.dialogRef.close();
});
} else if (this.mode === 'manual') {
this.dialogRef.close({
dataSource: 'MANUAL',
symbol: this.createAssetProfileForm.get('addSymbol').value
});
}
}
public ngOnDestroy() {}
public get showCurrencyErrorMessage() {
const addCurrencyFormControl =
this.createAssetProfileForm.get('addCurrency');
if (
addCurrencyFormControl.hasError('maxlength') ||
addCurrencyFormControl.hasError('minlength')
) {
return true;
}
return false;
}
public ngOnDestroy() {
this.unsubscribeSubject.next();
this.unsubscribeSubject.complete();
}
private atLeastOneValid(control: AbstractControl): ValidationErrors {
const addCurrencyControl = control.get('addCurrency');
const addSymbolControl = control.get('addSymbol');
const searchSymbolControl = control.get('searchSymbol');
if (addSymbolControl.valid && searchSymbolControl.valid) {
if (
addCurrencyControl.valid &&
addSymbolControl.valid &&
searchSymbolControl.valid
) {
return { atLeastOneValid: true };
}
if (
addCurrencyControl.valid ||
!addCurrencyControl ||
addSymbolControl.valid ||
!addSymbolControl ||
searchSymbolControl.valid ||
@ -89,4 +149,15 @@ export class CreateAssetProfileDialog implements OnInit, OnDestroy {
return { atLeastOneValid: true };
}
private initializeCustomCurrencies() {
this.adminService
.fetchAdminData()
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(({ settings }) => {
this.customCurrencies = settings[PROPERTY_CURRENCIES] as string[];
this.changeDetectorRef.markForCheck();
});
}
}

13
apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html

@ -17,6 +17,9 @@
<mat-radio-button class="ml-3" name="manual" value="manual">
</mat-radio-button>
<label class="m-0" for="manual" i18n>Add Manually</label>
<mat-radio-button class="ml-3" name="currency" value="currency">
</mat-radio-button>
<label class="m-0" for="currency" i18n>Add Currency</label>
</mat-radio-group>
</div>
@ -37,6 +40,16 @@
<input formControlName="addSymbol" matInput />
</mat-form-field>
</div>
} @else if (mode === 'currency') {
<div>
<mat-form-field appearance="outline" class="w-100">
<mat-label i18n>Currency</mat-label>
<input formControlName="addCurrency" matInput />
@if (showCurrencyErrorMessage) {
<mat-error i18n>Oops! Invalid currency.</mat-error>
}
</mat-form-field>
</div>
}
</div>
<div class="d-flex justify-content-end" mat-dialog-actions>

2
apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/interfaces/interfaces.ts

@ -2,3 +2,5 @@ export interface CreateAssetProfileDialogParams {
deviceType: string;
locale: string;
}
export type CreateAssetProfileDialogMode = 'auto' | 'currency' | 'manual';

5
apps/client/src/app/components/admin-overview/admin-overview.component.ts

@ -126,7 +126,10 @@ export class AdminOverviewComponent implements OnDestroy, OnInit {
if (currency) {
if (currency.length === 3) {
const currencies = uniq([...this.customCurrencies, currency]);
const currencies = uniq([
...this.customCurrencies,
currency.toUpperCase()
]);
this.putAdminSetting({ key: PROPERTY_CURRENCIES, value: currencies });
} else {
this.notificationService.alert({

6
apps/client/src/app/pages/faq/self-hosting/self-hosting-page.html

@ -52,8 +52,10 @@
</p>
<ol>
<li>Go to the <i>Admin Control</i> panel</li>
<li>Click on the <i>Add Currency</i> button</li>
<li>Insert e.g. <code>EUR</code> in the prompt</li>
<li>Go to the <i>Market Data</i> section</li>
<li>Click on the <i>+</i> button</li>
<li>Switch to <i>Add Currency</i></li>
<li>Insert e.g. <code>EUR</code> for Euro</li>
</ol>
</mat-card-content>
</mat-card>

64
package-lock.json

@ -40,7 +40,7 @@
"@nestjs/platform-express": "10.1.3",
"@nestjs/schedule": "3.0.2",
"@nestjs/serve-static": "4.0.0",
"@prisma/client": "5.19.1",
"@prisma/client": "5.20.0",
"@simplewebauthn/browser": "9.0.1",
"@simplewebauthn/server": "9.0.3",
"@stripe/stripe-js": "3.5.0",
@ -84,7 +84,7 @@
"passport": "0.7.0",
"passport-google-oauth20": "2.0.0",
"passport-jwt": "4.0.1",
"prisma": "5.19.1",
"prisma": "5.20.0",
"reflect-metadata": "0.1.13",
"rxjs": "7.5.6",
"stripe": "15.11.0",
@ -9646,9 +9646,9 @@
"dev": true
},
"node_modules/@prisma/client": {
"version": "5.19.1",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.19.1.tgz",
"integrity": "sha512-x30GFguInsgt+4z5I4WbkZP2CGpotJMUXy+Gl/aaUjHn2o1DnLYNTA+q9XdYmAQZM8fIIkvUiA2NpgosM3fneg==",
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.20.0.tgz",
"integrity": "sha512-CLv55ZuMuUawMsxoqxGtLT3bEZoa2W8L3Qnp6rDIFWy+ZBrUcOFKdoeGPSnbBqxc3SkdxJrF+D1veN/WNynZYA==",
"hasInstallScript": true,
"license": "Apache-2.0",
"engines": {
@ -9664,48 +9664,48 @@
}
},
"node_modules/@prisma/debug": {
"version": "5.19.1",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.19.1.tgz",
"integrity": "sha512-lAG6A6QnG2AskAukIEucYJZxxcSqKsMK74ZFVfCTOM/7UiyJQi48v6TQ47d6qKG3LbMslqOvnTX25dj/qvclGg==",
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.20.0.tgz",
"integrity": "sha512-oCx79MJ4HSujokA8S1g0xgZUGybD4SyIOydoHMngFYiwEwYDQ5tBQkK5XoEHuwOYDKUOKRn/J0MEymckc4IgsQ==",
"license": "Apache-2.0"
},
"node_modules/@prisma/engines": {
"version": "5.19.1",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.19.1.tgz",
"integrity": "sha512-kR/PoxZDrfUmbbXqqb8SlBBgCjvGaJYMCOe189PEYzq9rKqitQ2fvT/VJ8PDSe8tTNxhc2KzsCfCAL+Iwm/7Cg==",
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.20.0.tgz",
"integrity": "sha512-DtqkP+hcZvPEbj8t8dK5df2b7d3B8GNauKqaddRRqQBBlgkbdhJkxhoJTrOowlS3vaRt2iMCkU0+CSNn0KhqAQ==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "5.19.1",
"@prisma/engines-version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3",
"@prisma/fetch-engine": "5.19.1",
"@prisma/get-platform": "5.19.1"
"@prisma/debug": "5.20.0",
"@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284",
"@prisma/fetch-engine": "5.20.0",
"@prisma/get-platform": "5.20.0"
}
},
"node_modules/@prisma/engines-version": {
"version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3.tgz",
"integrity": "sha512-xR6rt+z5LnNqTP5BBc+8+ySgf4WNMimOKXRn6xfNRDSpHvbOEmd7+qAOmzCrddEc4Cp8nFC0txU14dstjH7FXA==",
"version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284.tgz",
"integrity": "sha512-Lg8AS5lpi0auZe2Mn4gjuCg081UZf88k3cn0RCwHgR+6cyHHpttPZBElJTHf83ZGsRNAmVCZCfUGA57WB4u4JA==",
"license": "Apache-2.0"
},
"node_modules/@prisma/fetch-engine": {
"version": "5.19.1",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.19.1.tgz",
"integrity": "sha512-pCq74rtlOVJfn4pLmdJj+eI4P7w2dugOnnTXpRilP/6n5b2aZiA4ulJlE0ddCbTPkfHmOL9BfaRgA8o+1rfdHw==",
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.20.0.tgz",
"integrity": "sha512-JVcaPXC940wOGpCOwuqQRTz6I9SaBK0c1BAyC1pcz9xBi+dzFgUu3G/p9GV1FhFs9OKpfSpIhQfUJE9y00zhqw==",
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "5.19.1",
"@prisma/engines-version": "5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3",
"@prisma/get-platform": "5.19.1"
"@prisma/debug": "5.20.0",
"@prisma/engines-version": "5.20.0-12.06fc58a368dc7be9fbbbe894adf8d445d208c284",
"@prisma/get-platform": "5.20.0"
}
},
"node_modules/@prisma/get-platform": {
"version": "5.19.1",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.19.1.tgz",
"integrity": "sha512-sCeoJ+7yt0UjnR+AXZL7vXlg5eNxaFOwC23h0KvW1YIXUoa7+W2ZcAUhoEQBmJTW4GrFqCuZ8YSP0mkDa4k3Zg==",
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.20.0.tgz",
"integrity": "sha512-8/+CehTZZNzJlvuryRgc77hZCWrUDYd/PmlZ7p2yNXtmf2Una4BWnTbak3us6WVdqoz5wmptk6IhsXdG2v5fmA==",
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "5.19.1"
"@prisma/debug": "5.20.0"
}
},
"node_modules/@redis/bloom": {
@ -28820,13 +28820,13 @@
}
},
"node_modules/prisma": {
"version": "5.19.1",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.19.1.tgz",
"integrity": "sha512-c5K9MiDaa+VAAyh1OiYk76PXOme9s3E992D7kvvIOhCrNsBQfy2mP2QAQtX0WNj140IgG++12kwZpYB9iIydNQ==",
"version": "5.20.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-5.20.0.tgz",
"integrity": "sha512-6obb3ucKgAnsGS9x9gLOe8qa51XxvJ3vLQtmyf52CTey1Qcez3A6W6ROH5HIz5Q5bW+0VpmZb8WBohieMFGpig==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/engines": "5.19.1"
"@prisma/engines": "5.20.0"
},
"bin": {
"prisma": "build/index.js"

4
package.json

@ -84,7 +84,7 @@
"@nestjs/platform-express": "10.1.3",
"@nestjs/schedule": "3.0.2",
"@nestjs/serve-static": "4.0.0",
"@prisma/client": "5.19.1",
"@prisma/client": "5.20.0",
"@simplewebauthn/browser": "9.0.1",
"@simplewebauthn/server": "9.0.3",
"@stripe/stripe-js": "3.5.0",
@ -128,7 +128,7 @@
"passport": "0.7.0",
"passport-google-oauth20": "2.0.0",
"passport-jwt": "4.0.1",
"prisma": "5.19.1",
"prisma": "5.20.0",
"reflect-metadata": "0.1.13",
"rxjs": "7.5.6",
"stripe": "15.11.0",

Loading…
Cancel
Save