From 46422f731ebde688aa8c833b71b893f0502207d1 Mon Sep 17 00:00:00 2001 From: Brandon Date: Thu, 19 Dec 2024 14:39:36 -0500 Subject: [PATCH] Feature/set up a notification service for prompt dialogs (#4117) * Set up a notification service for prompt dialogs * Update changelog --- CHANGELOG.md | 2 + .../portfolio-summary.component.ts | 22 +++++---- .../notification/interfaces/interfaces.ts | 9 ++++ .../core/notification/notification.service.ts | 37 ++++++++++++++- .../prompt-dialog/prompt-dialog.component.ts | 46 +++++++++++++++++++ .../prompt-dialog/prompt-dialog.html | 21 +++++++++ 6 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.component.ts create mode 100644 apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 87e5b50d9..d4aaf1573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a new static portfolio analysis rule: _Asset Class Cluster Risk_ (Equity) - Added a new static portfolio analysis rule: _Asset Class Cluster Risk_ (Fixed Income) +- Set up a notification service for prompt dialogs ### Changed +- Improved the usability to edit the emergency fund - Extracted the market data management from the admin control panel endpoint to a dedicated endpoint - Upgraded `big.js` from version `6.2.1` to `6.2.2` diff --git a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts index cac21c069..3189a338f 100644 --- a/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts +++ b/apps/client/src/app/components/portfolio-summary/portfolio-summary.component.ts @@ -1,3 +1,4 @@ +import { NotificationService } from '@ghostfolio/client/core/notification/notification.service'; import { getDateFnsLocale, getLocale } from '@ghostfolio/common/helper'; import { PortfolioSummary, User } from '@ghostfolio/common/interfaces'; import { translate } from '@ghostfolio/ui/i18n'; @@ -34,6 +35,8 @@ export class PortfolioSummaryComponent implements OnChanges { ); public timeInMarket: string; + public constructor(private notificationService: NotificationService) {} + public ngOnChanges() { if (this.summary) { if (this.summary.firstOrderDate) { @@ -49,14 +52,15 @@ export class PortfolioSummaryComponent implements OnChanges { } public onEditEmergencyFund() { - const emergencyFundInput = prompt( - $localize`Please enter the amount of your emergency fund:`, - this.summary.emergencyFund?.total?.toString() ?? '0' - ); - const emergencyFund = parseFloat(emergencyFundInput?.trim()); - - if (emergencyFund >= 0) { - this.emergencyFundChanged.emit(emergencyFund); - } + this.notificationService.prompt({ + confirmFn: (value) => { + const emergencyFund = parseFloat(value.trim()) || 0; + + this.emergencyFundChanged.emit(emergencyFund); + }, + confirmLabel: $localize`Save`, + defaultValue: this.summary.emergencyFund?.total?.toString() ?? '0', + title: $localize`Please set the amount of your emergency fund.` + }); } } diff --git a/apps/client/src/app/core/notification/interfaces/interfaces.ts b/apps/client/src/app/core/notification/interfaces/interfaces.ts index f5a526c92..f3546d457 100644 --- a/apps/client/src/app/core/notification/interfaces/interfaces.ts +++ b/apps/client/src/app/core/notification/interfaces/interfaces.ts @@ -17,3 +17,12 @@ export interface IConfirmParams { message?: string; title: string; } + +export interface IPromptParams { + confirmFn: (value: string) => void; + confirmLabel?: string; + defaultValue?: string; + discardLabel?: string; + title: string; + valueLabel?: string; +} diff --git a/apps/client/src/app/core/notification/notification.service.ts b/apps/client/src/app/core/notification/notification.service.ts index 189da67f5..1b58db64a 100644 --- a/apps/client/src/app/core/notification/notification.service.ts +++ b/apps/client/src/app/core/notification/notification.service.ts @@ -7,7 +7,12 @@ import { isFunction } from 'lodash'; import { GfAlertDialogComponent } from './alert-dialog/alert-dialog.component'; import { GfConfirmationDialogComponent } from './confirmation-dialog/confirmation-dialog.component'; import { ConfirmationDialogType } from './confirmation-dialog/confirmation-dialog.type'; -import { IAlertParams, IConfirmParams } from './interfaces/interfaces'; +import { + IAlertParams, + IConfirmParams, + IPromptParams +} from './interfaces/interfaces'; +import { GfPromptDialogComponent } from './prompt-dialog/prompt-dialog.component'; @Injectable() export class NotificationService { @@ -73,6 +78,36 @@ export class NotificationService { }); } + public prompt(aParams: IPromptParams) { + if (!aParams.confirmLabel) { + aParams.confirmLabel = translate('OK'); + } + + if (!aParams.discardLabel) { + aParams.discardLabel = translate('CANCEL'); + } + + const dialog = this.matDialog.open(GfPromptDialogComponent, { + autoFocus: true, + maxWidth: this.dialogMaxWidth, + width: this.dialogWidth + }); + + dialog.componentInstance.initialize({ + confirmLabel: aParams.confirmLabel, + defaultValue: aParams.defaultValue, + discardLabel: aParams.discardLabel, + title: aParams.title, + valueLabel: aParams.valueLabel + }); + + return dialog.afterClosed().subscribe((result: string) => { + if (result !== 'discard' && isFunction(aParams.confirmFn)) { + aParams.confirmFn(result); + } + }); + } + public setDialogMaxWidth(aDialogMaxWidth: string) { this.dialogMaxWidth = aDialogMaxWidth; } diff --git a/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.component.ts b/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.component.ts new file mode 100644 index 000000000..4f0c36005 --- /dev/null +++ b/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.component.ts @@ -0,0 +1,46 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; + +@Component({ + imports: [ + CommonModule, + FormsModule, + MatButtonModule, + MatDialogModule, + MatFormFieldModule, + MatInputModule + ], + selector: 'gf-prompt-dialog', + standalone: true, + templateUrl: './prompt-dialog.html' +}) +export class GfPromptDialogComponent { + public confirmLabel: string; + public defaultValue: string; + public discardLabel: string; + public title: string; + public value: string; + public valueLabel: string; + + public constructor(public dialogRef: MatDialogRef) {} + + public initialize(aParams: { + confirmLabel?: string; + defaultValue?: string; + discardLabel?: string; + title: string; + valueLabel?: string; + }) { + this.confirmLabel = aParams.confirmLabel; + this.defaultValue = aParams.defaultValue; + this.discardLabel = aParams.discardLabel; + this.title = aParams.title; + this.value = aParams.defaultValue; + this.valueLabel = aParams.valueLabel; + } +} diff --git a/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.html b/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.html new file mode 100644 index 000000000..5568cfdae --- /dev/null +++ b/apps/client/src/app/core/notification/prompt-dialog/prompt-dialog.html @@ -0,0 +1,21 @@ +@if (title) { +
+} + +
+ + @if (valueLabel) { + {{ valueLabel }} + } + + +
+ +
+ + +