Merge branch 'main' of gitea.suda.codes:giteauser/ghostfolio-mirror

This commit is contained in:
ksyasuda 2024-10-01 19:31:03 -07:00
commit a36404d375
25 changed files with 3956 additions and 2277 deletions

View File

@ -5,6 +5,16 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Added support to customize the rule thresholds in the _X-ray_ section (experimental)
### Changed
- Improved the language localization for German (`de`)
## 2.111.0 - 2024-09-28 ## 2.111.0 - 2024-09-28
### Added ### Added

View File

@ -1,4 +1,5 @@
import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code'; import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code';
import { PortfolioReportRule } from '@ghostfolio/common/interfaces';
import type { import type {
ColorScheme, ColorScheme,
DateRange, DateRange,

View File

@ -76,11 +76,11 @@ export class AccountClusterRiskCurrentInvestment extends Rule<Settings> {
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings { public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings {
return { return {
baseCurrency: aUserSettings.baseCurrency, baseCurrency,
isActive: aUserSettings.xRayRules[this.getKey()].isActive, isActive: xRayRules[this.getKey()].isActive,
thresholdMax: 0.5 thresholdMax: xRayRules[this.getKey()]?.thresholdMax ?? 0.5
}; };
} }
} }

View File

@ -34,9 +34,9 @@ export class AccountClusterRiskSingleAccount extends Rule<RuleSettings> {
}; };
} }
public getSettings(aUserSettings: UserSettings): RuleSettings { public getSettings({ xRayRules }: UserSettings): RuleSettings {
return { return {
isActive: aUserSettings.xRayRules[this.getKey()].isActive isActive: xRayRules[this.getKey()].isActive
}; };
} }
} }

View File

@ -62,10 +62,10 @@ export class CurrencyClusterRiskBaseCurrencyCurrentInvestment extends Rule<Setti
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings { public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings {
return { return {
baseCurrency: aUserSettings.baseCurrency, baseCurrency,
isActive: aUserSettings.xRayRules[this.getKey()].isActive isActive: xRayRules[this.getKey()].isActive
}; };
} }
} }

View File

@ -62,11 +62,11 @@ export class CurrencyClusterRiskCurrentInvestment extends Rule<Settings> {
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings { public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings {
return { return {
baseCurrency: aUserSettings.baseCurrency, baseCurrency,
isActive: aUserSettings.xRayRules[this.getKey()].isActive, isActive: xRayRules[this.getKey()].isActive,
thresholdMax: 0.5 thresholdMax: xRayRules[this.getKey()]?.thresholdMax ?? 0.5
}; };
} }
} }

View File

@ -19,7 +19,7 @@ export class EmergencyFundSetup extends Rule<Settings> {
} }
public evaluate(ruleSettings: Settings) { public evaluate(ruleSettings: Settings) {
if (this.emergencyFund < ruleSettings.thresholdMin) { if (!this.emergencyFund) {
return { return {
evaluation: 'No emergency fund has been set up', evaluation: 'No emergency fund has been set up',
value: false value: false
@ -32,16 +32,14 @@ export class EmergencyFundSetup extends Rule<Settings> {
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings { public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings {
return { return {
baseCurrency: aUserSettings.baseCurrency, baseCurrency,
isActive: aUserSettings.xRayRules[this.getKey()].isActive, isActive: xRayRules[this.getKey()].isActive
thresholdMin: 0
}; };
} }
} }
interface Settings extends RuleSettings { interface Settings extends RuleSettings {
baseCurrency: string; baseCurrency: string;
thresholdMin: number;
} }

View File

@ -43,11 +43,11 @@ export class FeeRatioInitialInvestment extends Rule<Settings> {
}; };
} }
public getSettings(aUserSettings: UserSettings): Settings { public getSettings({ baseCurrency, xRayRules }: UserSettings): Settings {
return { return {
baseCurrency: aUserSettings.baseCurrency, baseCurrency,
isActive: aUserSettings.xRayRules[this.getKey()].isActive, isActive: xRayRules[this.getKey()].isActive,
thresholdMax: 0.01 thresholdMax: xRayRules[this.getKey()]?.thresholdMax ?? 0.01
}; };
} }
} }

View File

@ -2,6 +2,7 @@ import { PortfolioReportRule } from '@ghostfolio/common/interfaces';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { Component, Inject } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { import {
MAT_DIALOG_DATA, MAT_DIALOG_DATA,
@ -16,6 +17,7 @@ import { IRuleSettingsDialogParams } from './interfaces/interfaces';
@Component({ @Component({
imports: [ imports: [
CommonModule, CommonModule,
FormsModule,
MatButtonModule, MatButtonModule,
MatDialogModule, MatDialogModule,
MatFormFieldModule, MatFormFieldModule,

View File

@ -1,23 +1,37 @@
<div mat-dialog-title>{{ data.rule.name }}</div> <div mat-dialog-title>{{ data.rule.name }}</div>
<div class="py-3" mat-dialog-content> <div class="py-3" mat-dialog-content>
<mat-form-field appearance="outline" class="w-100"> <mat-form-field
appearance="outline"
class="w-100"
[ngClass]="{ 'd-none': settings.thresholdMin === undefined }"
>
<mat-label i18n>Threshold Min</mat-label> <mat-label i18n>Threshold Min</mat-label>
<input matInput name="thresholdMin" type="number" /> <input
matInput
name="thresholdMin"
type="number"
[(ngModel)]="settings.thresholdMin"
/>
</mat-form-field> </mat-form-field>
<mat-form-field appearance="outline" class="w-100"> <mat-form-field
appearance="outline"
class="w-100"
[ngClass]="{ 'd-none': settings.thresholdMax === undefined }"
>
<mat-label i18n>Threshold Max</mat-label> <mat-label i18n>Threshold Max</mat-label>
<input matInput name="thresholdMax" type="number" /> <input
matInput
name="thresholdMax"
type="number"
[(ngModel)]="settings.thresholdMax"
/>
</mat-form-field> </mat-form-field>
</div> </div>
<div align="end" mat-dialog-actions> <div align="end" mat-dialog-actions>
<button i18n mat-button (click)="dialogRef.close()">Close</button> <button i18n mat-button (click)="dialogRef.close()">Close</button>
<button <button color="primary" mat-flat-button (click)="dialogRef.close(settings)">
color="primary"
mat-flat-button
(click)="dialogRef.close({ settings })"
>
<ng-container i18n>Save</ng-container> <ng-container i18n>Save</ng-container>
</button> </button>
</div> </div>

View File

@ -62,7 +62,7 @@
<ion-icon name="ellipsis-horizontal" /> <ion-icon name="ellipsis-horizontal" />
</button> </button>
<mat-menu #rulesMenu="matMenu" xPosition="before"> <mat-menu #rulesMenu="matMenu" xPosition="before">
@if (rule?.isActive && !isEmpty(rule.settings) && false) { @if (rule?.isActive && !isEmpty(rule.settings)) {
<button mat-menu-item (click)="onCustomizeRule(rule)"> <button mat-menu-item (click)="onCustomizeRule(rule)">
<ng-container i18n>Customize</ng-container>... <ng-container i18n>Customize</ng-container>...
</button> </button>

View File

@ -55,16 +55,15 @@ export class RuleComponent implements OnInit {
dialogRef dialogRef
.afterClosed() .afterClosed()
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe( .subscribe((settings: PortfolioReportRule['settings']) => {
({ settings }: { settings: PortfolioReportRule['settings'] }) => {
if (settings) { if (settings) {
console.log(settings); this.ruleUpdated.emit({
xRayRules: {
// TODO [rule.key]: settings
// this.ruleUpdated.emit(settings);
} }
});
} }
); });
} }
public onUpdateRule(rule: PortfolioReportRule) { public onUpdateRule(rule: PortfolioReportRule) {

View File

@ -134,8 +134,6 @@ export class FirePageComponent implements OnDestroy, OnInit {
} }
public onRulesUpdated(event: UpdateUserSettingDto) { public onRulesUpdated(event: UpdateUserSettingDto) {
this.isLoading = true;
this.dataService this.dataService
.putUserSetting(event) .putUserSetting(event)
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
import { PortfolioReportRule } from '@ghostfolio/common/interfaces';
export type XRayRulesSettings = { export type XRayRulesSettings = {
AccountClusterRiskCurrentInvestment?: RuleSettings; AccountClusterRiskCurrentInvestment?: RuleSettings;
AccountClusterRiskSingleAccount?: RuleSettings; AccountClusterRiskSingleAccount?: RuleSettings;
@ -7,6 +9,6 @@ export type XRayRulesSettings = {
FeeRatioInitialInvestment?: RuleSettings; FeeRatioInitialInvestment?: RuleSettings;
}; };
interface RuleSettings { interface RuleSettings extends Pick<PortfolioReportRule, 'settings'> {
isActive: boolean; isActive: boolean;
} }