From 5ee0a3fc53a285d8264e91437577ffc005db72a7 Mon Sep 17 00:00:00 2001 From: Nitesh Yadav Date: Thu, 24 Sep 2026 18:20:11 +0530 Subject: [PATCH] feat(fd): show gap-interest renewal preview in Payment dialog before confirming a late renewal Fetches current balance, gap days since maturity, the linked savings account's rate, and the resulting gap-interest amount and projected new balance, and displays them above the dashboard-renewal timing text -- so staff see exactly what a late renewal is about to credit before confirming anything. Read-only; failure to load the preview doesn't block the renewal itself, since the existing timing text already explains the action. Co-Authored-By: Claude Sonnet 5 --- .../payment-dialog.component.html | 38 ++++++++++++ .../payment-dialog.component.scss | 62 +++++++++++++++++++ .../payment-dialog.component.ts | 19 ++++++ .../fixed-deposits/fixed-deposits.service.ts | 7 +++ 4 files changed, 126 insertions(+) diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.html b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.html index 34a510e7f..870c04a21 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.html +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.html @@ -45,6 +45,44 @@ {{ dashboardRenewalTimingText }}

+ + @if (renewalPreviewLoading) { +

+ + Calculating gap-period interest... +

+ } @else if (renewalPreview && !renewalPreview.withinGrace) { + @if (!renewalPreview.linkedAccountId) { + + } @else { +
+
+ Current Balance + {{ renewalPreview.currentBalance | formatNumber }} +
+
+ Gap Days ({{ renewalPreview.maturityDate }} → today) + {{ renewalPreview.gapDays }} +
+
+ Savings Account Interest Rate + {{ renewalPreview.linkedAccountRate }}% +
+
+ Gap-Period Interest to be Credited + + {{ renewalPreview.gapInterestAmount | formatNumber }} +
+
+ New Balance for the Renewed Term + {{ renewalPreview.projectedBalance | formatNumber }} +
+
+ } + } } @if (reinvestCap !== null && reinvestCap !== undefined) { diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.scss b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.scss index a95aa35aa..c6c8920ad 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.scss +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.scss @@ -164,6 +164,68 @@ } } +.pmt__preview { + display: flex; + flex-direction: column; + background: #fff; + border: 1px solid #e2e8f0; + border-radius: 14px; + padding: 4px 18px; + margin: 0 0 18px; + + &-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 11px 0; + border-bottom: 1px solid #f1f5f9; + + &:last-child { + border-bottom: none; + } + + &--highlight { + .pmt__preview-value { + color: #0d9488; + font-weight: 800; + } + } + + &--total { + margin: 0 -18px; + padding: 14px 18px; + background: linear-gradient(135deg, #eff6ff 0%, #e0e9ff 100%); + border-bottom: none; + border-radius: 0 0 12px 12px; + + .pmt__preview-label { + color: #1d4ed8; + font-weight: 700; + } + + .pmt__preview-value { + color: #0f172a; + font-size: 18px; + font-weight: 800; + } + } + } + + &-label { + font-size: 12.5px; + color: #64748b; + font-weight: 600; + } + + &-value { + font-family: 'JetBrains Mono', 'DM Sans', monospace; + font-size: 14px; + font-weight: 700; + color: #0f172a; + } +} + .pmt__section-label { font-size: 12px; font-weight: 700; diff --git a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.ts b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.ts index 4e1cc8c39..789d1e902 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.ts +++ b/src/app/deposits/fixed-deposits/fixed-deposit-account-view/custom-dialogs/payment-dialog/payment-dialog.component.ts @@ -113,6 +113,12 @@ export class PaymentDialogComponent implements OnInit { * after open; cleared as soon as `submit()` has credited it, so a retried submit doesn't double-post. */ private gapInterestPending = false; + /** Read-only numbers (current balance, linked account's rate, gap days, gap-interest amount, + * projected new balance) fetched once on open in dashboard-renewal mode, shown above the Action + * cards so staff see exactly what a late renewal is about to credit before confirming anything. */ + renewalPreview: any = null; + renewalPreviewLoading = false; + ngOnInit(): void { this.maxDate = this.settingsService.businessDate; this.paymentForm = this.formBuilder.group({ @@ -136,6 +142,19 @@ export class PaymentDialogComponent implements OnInit { .get('closedOnDate') .setValue(dashboardRow.withinGraceWindow ? new Date(dashboardRow.maturityDate) : this.maxDate); this.gapInterestPending = !dashboardRow.withinGraceWindow; + + this.renewalPreviewLoading = true; + this.fixedDepositsService.getFdRenewalPreview(this.data.accountId).subscribe({ + next: (preview: any) => { + this.renewalPreview = preview; + this.renewalPreviewLoading = false; + }, + // Non-fatal -- the plain timing text above still explains what will happen even if the + // numeric breakdown couldn't be fetched; only the numbers are missing, not the action itself. + error: () => { + this.renewalPreviewLoading = false; + } + }); } this.paymentForm.get('onAccountClosureId').valueChanges.subscribe((id: number) => this.onClosureTypeChange(id)); diff --git a/src/app/deposits/fixed-deposits/fixed-deposits.service.ts b/src/app/deposits/fixed-deposits/fixed-deposits.service.ts index 79976fe80..e5af75631 100644 --- a/src/app/deposits/fixed-deposits/fixed-deposits.service.ts +++ b/src/app/deposits/fixed-deposits/fixed-deposits.service.ts @@ -174,6 +174,13 @@ export class FixedDepositsService { return this.http.post(`/fixeddepositaccounts/${accountId}/credit-gap-interest`, {}); } + /** Read-only -- what a Renew is about to do (current balance, linked account's rate, gap days, + * gap-interest amount, projected new balance), shown in the Payment dialog before anything is + * confirmed. Posts nothing; safe to call as often as the dialog needs. */ + getFdRenewalPreview(accountId: string | number): Observable { + return this.http.get(`/fixeddepositaccounts/${accountId}/renewal-preview`); + } + /** * @param fixedDepositAccount Fixed Deposit Account * @returns {Observable}