feat: account charges UI — current account, FD, RD stepper redesign

Redesigned create-account stepper UI for current accounts, fixed deposits,
and recurring deposits: updated layout, preview steps, interest rate chart
step, currency step, and detail step components. Savings preview styling
and currency step also updated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-05-22 12:47:25 +05:30
co-authored by Claude Sonnet 4.6
parent 64c76a9806
commit 3370a95235
34 changed files with 1154 additions and 452 deletions
@@ -6,104 +6,143 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/. file, You can obtain one at http://mozilla.org/MPL/2.0/.
--> -->
<div class="container"> <div class="cca-container">
<mat-stepper <!-- Hero -->
orientation="vertical" <div class="cca-header">
[linear]="false" <fa-icon icon="university" class="cca-header__icon"></fa-icon>
class="mat-elevation-z8" <div>
labelPosition="bottom" <h2 class="cca-header__title">Create Current Account</h2>
#currentAccountProductStepper <div class="cca-header__sub">
Pick a product, confirm currency, set digital channels and review before submit.
</div>
</div>
</div>
<!-- Wizard tabs -->
<mat-tab-group
class="cca-tabs"
animationDuration="200ms"
[preserveContent]="true"
[(selectedIndex)]="selectedTabIndex"
> >
<ng-template matStepperIcon="number"> <!-- Tab 1: Product Classification + Details -->
<fa-icon icon="pencil-alt" size="sm"></fa-icon> <mat-tab>
</ng-template> <ng-template mat-tab-label>
<fa-icon icon="check-square" class="cca-tab__icon"></fa-icon>
<ng-template matStepperIcon="edit"> Product
<fa-icon icon="pencil-alt" size="sm"></fa-icon> </ng-template>
</ng-template> <div class="cca-tab-body">
<mifosx-current-account-details-step
<ng-template matStepperIcon="done">
<fa-icon icon="check" size="sm"></fa-icon>
</ng-template>
<ng-template matStepperIcon="error">
<fa-icon icon="exclamation-triangle" size="lg"></fa-icon>
</ng-template>
<ng-template matStepperIcon="preview">
<fa-icon icon="eye" size="sm"></fa-icon>
</ng-template>
<!-- Step 1: Details -->
<mat-step>
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
<mifosx-current-account-details-step
[currentAccountTemplate]="currentAccountTemplate"
(currentAccountProductTemplate)="setTemplate($event)"
>
</mifosx-current-account-details-step>
</mat-step>
@if (currentAccountProductTemplate) {
<!-- Step 2: Currency -->
<mat-step>
<ng-template matStepLabel>CURRENCY</ng-template>
<mifosx-current-account-currency-step
[currentAccountProductTemplate]="currentAccountProductTemplate"
[primaryClientId]="currentAccountTemplate?.clientId ?? null"
></mifosx-current-account-currency-step>
<div class="layout-row align-center gap-2percent margin-t responsive-column">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<button mat-raised-button matStepperNext>
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</mat-step>
<!-- Step 3: Digital -->
<mat-step>
<ng-template matStepLabel>DIGITAL</ng-template>
<mifosx-current-account-digital-step
[currentAccountProductTemplate]="currentAccountProductTemplate"
></mifosx-current-account-digital-step>
<div class="layout-row align-center gap-2percent margin-t responsive-column">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<button mat-raised-button matStepperNext>
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</mat-step>
<!-- Step 4: Preview -->
<mat-step completed>
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template>
<mifosx-current-account-preview-step
[currentAccountProductTemplate]="currentAccountProductTemplate"
[currentAccountTemplate]="currentAccountTemplate" [currentAccountTemplate]="currentAccountTemplate"
[currentAccountTermForm]="currentAccountTermForm" [hideButtons]="true"
[currentAccountSettingsForm]="currentAccountSettingsForm" (currentAccountProductTemplate)="setTemplate($event)"
[currentAccountTermsForm]="currentAccountTermsForm"
[currentAccount]="currentAccount"
[currentAccountDigital]="currentAccountDigital"
[currentAccountNominees]="currentAccountCurrencyStep?.nominees ?? []"
[currentAccountCurrencyFlags]="currentAccountCurrencyFlags"
(submitEvent)="submit()"
> >
</mifosx-current-account-preview-step> </mifosx-current-account-details-step>
</mat-step>
} <div class="cca-actions">
</mat-stepper> <span class="cca-actions__spacer"></span>
<button mat-raised-button color="primary" [disabled]="!currentAccountProductTemplate" (click)="next()">
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</div>
</mat-tab>
<!-- Tab 2: Currency -->
<mat-tab [disabled]="!currentAccountProductTemplate">
<ng-template mat-tab-label>
<fa-icon icon="dollar-sign" class="cca-tab__icon"></fa-icon>
Currency
</ng-template>
<div class="cca-tab-body">
@if (currentAccountProductTemplate) {
<mifosx-current-account-currency-step
[currentAccountProductTemplate]="currentAccountProductTemplate"
[primaryClientId]="currentAccountTemplate?.clientId ?? null"
></mifosx-current-account-currency-step>
}
<div class="cca-actions">
<button mat-stroked-button (click)="prev()">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<span class="cca-actions__spacer"></span>
<button mat-raised-button color="primary" (click)="next()">
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</div>
</mat-tab>
<!-- Tab 3: Digital -->
<mat-tab [disabled]="!currentAccountProductTemplate">
<ng-template mat-tab-label>
<fa-icon icon="mobile-alt" class="cca-tab__icon"></fa-icon>
Digital
</ng-template>
<div class="cca-tab-body">
@if (currentAccountProductTemplate) {
<mifosx-current-account-digital-step
[currentAccountProductTemplate]="currentAccountProductTemplate"
></mifosx-current-account-digital-step>
}
<div class="cca-actions">
<button mat-stroked-button (click)="prev()">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<span class="cca-actions__spacer"></span>
<button mat-raised-button color="primary" (click)="next()">
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</div>
</mat-tab>
<!-- Tab 4: Preview -->
<mat-tab [disabled]="!currentAccountProductTemplate">
<ng-template mat-tab-label>
<fa-icon icon="eye" class="cca-tab__icon"></fa-icon>
{{ 'labels.inputs.PREVIEW' | translate }}
</ng-template>
<div class="cca-tab-body">
@if (currentAccountProductTemplate) {
<mifosx-current-account-preview-step
[currentAccountProductTemplate]="currentAccountProductTemplate"
[currentAccountTemplate]="currentAccountTemplate"
[currentAccountTermForm]="currentAccountTermForm"
[currentAccountSettingsForm]="currentAccountSettingsForm"
[currentAccountTermsForm]="currentAccountTermsForm"
[currentAccount]="currentAccount"
[currentAccountDigital]="currentAccountDigital"
[currentAccountNominees]="currentAccountCurrencyStep?.nominees ?? []"
[currentAccountCurrencyFlags]="currentAccountCurrencyFlags"
[hideButtons]="true"
(submitEvent)="submit()"
>
</mifosx-current-account-preview-step>
}
<div class="cca-actions">
<button mat-stroked-button (click)="prev()">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<span class="cca-actions__spacer"></span>
<button mat-stroked-button [routerLink]="['../..']">
{{ 'labels.buttons.Cancel' | translate }}
</button>
<button mat-raised-button color="primary" class="preview-submit-row__primary" (click)="submit()">
<fa-icon icon="check" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Submit' | translate }}
</button>
</div>
</div>
</mat-tab>
</mat-tab-group>
</div> </div>
@@ -6,75 +6,165 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
// Container styling with proper spacing and margins $navy: #0d1b2a;
.container { $blue: #1e88e5;
margin: 1.5rem auto; $blue-d: #1e40af;
max-width: 75rem; $indigo: #4f46e5;
width: 100%; $border: #e2e8f0;
padding: 0 1.5rem;
box-sizing: border-box; .cca-container {
max-width: 1180px;
margin: 0 auto;
padding: 20px 24px;
} }
// Material Stepper styling with proper spacing /* ─── Hero header (matches Nexora pages) ────────────────────────────── */
:host ::ng-deep mat-stepper { .cca-header {
background: transparent; display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
.mat-step-header { &__icon {
padding: 1rem 1.5rem; width: 44px;
margin-bottom: 0.5rem; height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background: linear-gradient(135deg, $blue 0%, $indigo 100%);
color: #fff;
font-size: 18px;
box-shadow: 0 2px 8px rgb(29 78 216 / 25%);
flex-shrink: 0;
} }
.mat-step-content { &__title {
padding: 1.5rem; margin: 0;
margin: 1rem 0; font-size: 20px;
overflow: visible !important; font-weight: 800;
color: $navy;
line-height: 1.1;
letter-spacing: 0.02em;
}
&__sub {
margin-top: 2px;
font-size: 12.5px;
color: #64748b;
} }
} }
// Form field spacing within steps /* ─── Wizard tab strip ──────────────────────────────────────────────── */
:host ::ng-deep mat-step-content { .cca-tabs {
overflow: visible !important; background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
mat-form-field { ::ng-deep {
margin-bottom: 1rem; .mat-mdc-tab-header {
width: 100%; background: #f8fafc;
} border-bottom: 1px solid $border;
.layout-row-wrap {
gap: 1rem;
margin-bottom: 1rem;
}
}
// Responsive design
@media (width <= 768px) {
.container {
padding: 0 1rem;
margin: 1rem auto;
}
:host ::ng-deep mat-stepper {
.mat-step-content {
padding: 1rem;
margin: 0.75rem 0;
} }
.mat-step-header { .mat-mdc-tab {
padding: 0.75rem 1rem; min-width: 140px;
height: 50px;
padding: 0 22px;
font-size: 13px !important;
font-weight: 600 !important;
letter-spacing: 0.01em;
color: #475569 !important;
opacity: 1 !important;
}
.mat-mdc-tab:hover:not(.mdc-tab--active, .mat-mdc-tab-disabled) {
background: rgb(30 64 175 / 5%);
color: $navy !important;
}
.mat-mdc-tab.mdc-tab--active {
.mdc-tab__text-label {
color: $blue-d !important;
font-weight: 700 !important;
}
}
/* Active-tab underline indicator — slightly bolder than Material's default. */
.mdc-tab-indicator__content--underline {
border-color: $blue-d !important;
border-top-width: 3px !important;
}
.mat-mdc-tab.mat-mdc-tab-disabled {
opacity: 0.45 !important;
cursor: not-allowed;
}
.mat-mdc-tab-body-wrapper {
min-height: 320px;
}
.mat-mdc-tab-body,
.mat-mdc-tab-body-content {
overflow: visible !important;
} }
} }
} }
@media (width <= 480px) { .cca-tab__icon {
.container { margin-right: 8px;
padding: 0 0.75rem; font-size: 13px;
margin: 0.75rem auto; }
}
:host ::ng-deep mat-stepper { /* ─── Tab body + action bar ─────────────────────────────────────────── */
.mat-step-content { .cca-tab-body {
padding: 0.75rem; padding: 24px;
margin: 0.5rem 0;
} @media (width <= 768px) {
padding: 16px;
} }
} }
.cca-actions {
display: flex;
align-items: center;
gap: 12px;
margin-top: 24px;
padding-top: 16px;
border-top: 1px solid #f1f5f9;
&__spacer {
flex: 1;
}
button {
height: 40px !important;
border-radius: 10px !important;
font-weight: 600 !important;
padding: 0 18px !important;
}
}
/* Utility helpers preserved from the prior stepper template. */
.m-l-10 {
margin-left: 8px;
}
.m-r-10 {
margin-right: 8px;
}
button.preview-submit-row__primary,
.mat-mdc-raised-button.mat-primary.preview-submit-row__primary,
.preview-submit-row__primary {
background: linear-gradient(135deg, #10b981 0%, #047857 100%) !important;
--mdc-protected-button-container-color: transparent !important;
--mdc-filled-button-container-color: transparent !important;
color: #fff !important;
box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important;
}
@@ -20,14 +20,7 @@ import { CurrentAccountsService } from '../current-accounts.service';
import { SettingsService } from 'app/settings/settings.service'; import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates'; import { Dates } from 'app/core/utils/dates';
import { AlertService } from 'app/core/alert/alert.service'; import { AlertService } from 'app/core/alert/alert.service';
import { import { MatTabsModule } from '@angular/material/tabs';
MatStepper,
MatStepperIcon,
MatStep,
MatStepLabel,
MatStepperNext,
MatStepperPrevious
} from '@angular/material/stepper';
import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { CurrentAccountPreviewStepComponent } from '../current-account-stepper/current-account-preview-step/current-account-preview-step.component'; import { CurrentAccountPreviewStepComponent } from '../current-account-stepper/current-account-preview-step/current-account-preview-step.component';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@@ -42,13 +35,8 @@ import { FormGroup } from '@angular/forms';
styleUrls: ['./create-current-account.component.scss'], styleUrls: ['./create-current-account.component.scss'],
imports: [ imports: [
...STANDALONE_SHARED_IMPORTS, ...STANDALONE_SHARED_IMPORTS,
MatStepper, MatTabsModule,
MatStepperIcon,
MatStepperNext,
MatStepperPrevious,
FaIconComponent, FaIconComponent,
MatStep,
MatStepLabel,
CurrentAccountDetailsStepComponent, CurrentAccountDetailsStepComponent,
CurrentAccountCurrencyStepComponent, CurrentAccountCurrencyStepComponent,
CurrentAccountDigitalStepComponent, CurrentAccountDigitalStepComponent,
@@ -67,6 +55,10 @@ export class CreateCurrentAccountComponent {
currentAccountTemplate: any; currentAccountTemplate: any;
/** Current Account Product Template */ /** Current Account Product Template */
currentAccountProductTemplate: any; currentAccountProductTemplate: any;
/** Currently visible wizard tab (0-indexed). */
selectedTabIndex = 0;
/** Total wizard tabs — Product, Currency, Digital, Preview. */
private readonly totalTabs = 4;
@ViewChild(CurrentAccountDetailsStepComponent, { static: true }) @ViewChild(CurrentAccountDetailsStepComponent, { static: true })
currentAccountDetailsStep: CurrentAccountDetailsStepComponent; currentAccountDetailsStep: CurrentAccountDetailsStepComponent;
@@ -90,6 +82,18 @@ export class CreateCurrentAccountComponent {
this.currentAccountProductTemplate = $event; this.currentAccountProductTemplate = $event;
} }
next(): void {
if (this.selectedTabIndex < this.totalTabs - 1) {
this.selectedTabIndex += 1;
}
}
prev(): void {
if (this.selectedTabIndex > 0) {
this.selectedTabIndex -= 1;
}
}
/** /**
* Retrieves current account details form. * Retrieves current account details form.
*/ */
@@ -76,6 +76,13 @@ export class CurrentAccountCurrencyStepComponent implements OnChanges {
this.nomineeError = 'This person is already added as a nominee.'; this.nomineeError = 'This person is already added as a nominee.';
return; return;
} }
const relationshipLower = data.relationshipName?.toLowerCase();
if (relationshipLower === 'father' || relationshipLower === 'mother' || relationshipLower === 'wife') {
if (this.nominees.some((n) => n.relationshipName?.toLowerCase() === relationshipLower)) {
this.nomineeError = `Only one ${data.relationshipName} can be added as a nominee.`;
return;
}
}
const newTotal = this.totalShare + (+data.percentageShare || 0); const newTotal = this.totalShare + (+data.percentageShare || 0);
if (newTotal > 100) { if (newTotal > 100) {
this.nomineeError = `Total share cannot exceed 100%. Remaining: ${this.remainingShare}%`; this.nomineeError = `Total share cannot exceed 100%. Remaining: ${this.remainingShare}%`;
@@ -933,14 +933,16 @@
</div> </div>
} }
<div class="layout-row align-center gap-2percent margin-t responsive-column"> @if (!hideButtons) {
<button mat-raised-button matStepperPrevious disabled> <div class="layout-row align-center gap-2percent margin-t responsive-column">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon> <button mat-raised-button matStepperPrevious disabled>
{{ 'labels.buttons.Previous' | translate }} <fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
</button> {{ 'labels.buttons.Previous' | translate }}
<button mat-raised-button matStepperNext [disabled]="currentAccountDetailsForm.invalid"> </button>
{{ 'labels.buttons.Next' | translate }} <button mat-raised-button matStepperNext [disabled]="currentAccountDetailsForm.invalid">
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon> {{ 'labels.buttons.Next' | translate }}
</button> <fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</div> </button>
</div>
}
</form> </form>
@@ -52,6 +52,7 @@ export class CurrentAccountDetailsStepComponent implements OnInit {
private cdr = inject(ChangeDetectorRef); private cdr = inject(ChangeDetectorRef);
@Input() currentAccountTemplate: any; @Input() currentAccountTemplate: any;
@Input() hideButtons = false;
minDate = new Date(2000, 0, 1); minDate = new Date(2000, 0, 1);
maxDate = new Date(); maxDate = new Date();
@@ -643,15 +643,18 @@
} }
</div> </div>
<div class="layout-row responsive-column align-center gap-2px margin-t"> @if (!hideButtons) {
<button mat-raised-button matStepperPrevious> <div class="layout-row responsive-column align-center gap-2px margin-t">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon> <button mat-raised-button matStepperPrevious>
{{ 'labels.buttons.Previous' | translate }} <fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
</button> {{ 'labels.buttons.Previous' | translate }}
<button mat-raised-button [routerLink]="['../..']"> </button>
{{ 'labels.buttons.Cancel' | translate }} <button mat-raised-button [routerLink]="['../..']">
</button> {{ 'labels.buttons.Cancel' | translate }}
<button mat-raised-button color="primary" (click)="submitEvent.emit()"> </button>
{{ 'labels.buttons.Submit' | translate }} <button mat-raised-button color="primary" class="preview-submit-row__primary" (click)="submitEvent.emit()">
</button> <fa-icon icon="check" class="m-r-10"></fa-icon>
</div> {{ 'labels.buttons.Submit' | translate }}
</button>
</div>
}
@@ -459,3 +459,15 @@
font-style: italic; font-style: italic;
margin: 0; margin: 0;
} }
button.preview-submit-row__primary,
.mat-mdc-raised-button.mat-primary.preview-submit-row__primary,
.preview-submit-row__primary {
background: linear-gradient(135deg, #10b981 0%, #047857 100%) !important;
--mdc-protected-button-container-color: transparent !important;
--mdc-filled-button-container-color: transparent !important;
color: #fff !important;
box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important;
}
@@ -85,6 +85,7 @@ export class CurrentAccountPreviewStepComponent implements OnChanges {
@Input() currentAccountProductTemplate: any; @Input() currentAccountProductTemplate: any;
@Input() currentAccountTemplate: any; @Input() currentAccountTemplate: any;
@Input() hideButtons = false;
@Input() currentAccountTermsForm: any; @Input() currentAccountTermsForm: any;
@Input() currentAccountTermForm: any; @Input() currentAccountTermForm: any;
@Input() currentAccountSettingsForm: any; @Input() currentAccountSettingsForm: any;
@@ -6,103 +6,140 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/. file, You can obtain one at http://mozilla.org/MPL/2.0/.
--> -->
<div class="container"> <div class="cfa-container">
<mat-stepper <!-- Hero Header -->
orientation="vertical" <div class="cfa-header">
[linear]="false" <fa-icon icon="university" class="cfa-header__icon"></fa-icon>
class="mat-elevation-z8" <div>
labelPosition="bottom" <h2 class="cfa-header__title">Create Fixed Deposit Account</h2>
#fixedDepositAccountStepper <div class="cfa-header__sub">Pick a product, confirm currency, set terms and review before submit.</div>
</div>
</div>
<!-- Wizard tabs -->
<mat-tab-group
class="cfa-tabs"
animationDuration="200ms"
[preserveContent]="true"
[(selectedIndex)]="selectedTabIndex"
> >
<ng-template matStepperIcon="number"> <!-- Tab 1: Product Classification + Details -->
<fa-icon icon="pencil-alt" size="sm"></fa-icon> <mat-tab>
</ng-template> <ng-template mat-tab-label>
<fa-icon icon="check-square" class="cfa-tab__icon"></fa-icon>
<ng-template matStepperIcon="edit"> Product
<fa-icon icon="pencil-alt" size="sm"></fa-icon> </ng-template>
</ng-template> <div class="cfa-tab-body">
<mifosx-fixed-deposit-account-details-step
<ng-template matStepperIcon="done">
<fa-icon icon="check" size="sm"></fa-icon>
</ng-template>
<ng-template matStepperIcon="error">
<fa-icon icon="exclamation-triangle" size="lg"></fa-icon>
</ng-template>
<ng-template matStepperIcon="preview">
<fa-icon icon="eye" size="sm"></fa-icon>
</ng-template>
<!-- Step 1: Details -->
<mat-step>
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
<mifosx-fixed-deposit-account-details-step
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
(fixedDepositsAccountProductTemplate)="setTemplate($event)"
>
</mifosx-fixed-deposit-account-details-step>
</mat-step>
<!-- Step 2: Currency -->
@if (fixedDepositsAccountProductTemplate) {
<mat-step>
<ng-template matStepLabel>Currency</ng-template>
<mifosx-fixed-deposit-account-currency-step
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
[primaryClientId]="fixedDepositsAccountTemplate?.clientId ?? null"
></mifosx-fixed-deposit-account-currency-step>
<div class="layout-row align-center gap-2percent margin-t responsive-column">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<button mat-raised-button matStepperNext>
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</mat-step>
<!-- Step 4: Interest Rate Chart -->
<mat-step>
<ng-template matStepLabel>{{ 'labels.inputs.INTEREST RATE CHART' | translate }}</ng-template>
<mifosx-fixed-deposit-account-interest-rate-chart-step
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate" [fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate" (fixedDepositsAccountProductTemplate)="setTemplate($event)"
[cbsBaseRates]="cbsBaseRates" [hideButtons]="true"
(tenureSelected)="onTenureSelected($event)"
> >
</mifosx-fixed-deposit-account-interest-rate-chart-step> </mifosx-fixed-deposit-account-details-step>
<div class="layout-row align-center gap-2percent margin-t responsive-column"> <div class="cfa-actions">
<button mat-raised-button matStepperPrevious> <span class="cfa-actions__spacer"></span>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon> <button mat-raised-button color="primary" [disabled]="!fixedDepositsAccountProductTemplate" (click)="next()">
{{ 'labels.buttons.Previous' | translate }}
</button>
<button mat-raised-button matStepperNext>
{{ 'labels.buttons.Next' | translate }} {{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon> <fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button> </button>
</div> </div>
</mat-step> </div>
</mat-tab>
<!-- Step 6: Preview --> <!-- Tab 2: Currency -->
<mat-step state="preview" completed> <mat-tab [disabled]="!fixedDepositsAccountProductTemplate">
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template> <ng-template mat-tab-label>
<mifosx-fixed-deposit-account-preview-step <fa-icon icon="dollar-sign" class="cfa-tab__icon"></fa-icon>
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate" Currency
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate" </ng-template>
[fixedDepositAccountData]="fixedDepositAccount" <div class="cfa-tab-body">
[selectedTenure]="selectedTenure" @if (fixedDepositsAccountProductTemplate) {
[fdNominees]="fixedDepositAccountCurrencyStep?.nominees ?? []" <mifosx-fixed-deposit-account-currency-step
(submitEvent)="submit()" [fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
></mifosx-fixed-deposit-account-preview-step> [primaryClientId]="fixedDepositsAccountTemplate?.clientId ?? null"
</mat-step> ></mifosx-fixed-deposit-account-currency-step>
} }
</mat-stepper>
<div class="cfa-actions">
<button mat-stroked-button (click)="prev()">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<span class="cfa-actions__spacer"></span>
<button mat-raised-button color="primary" (click)="next()">
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</div>
</mat-tab>
<!-- Tab 3: Interest Rate Chart -->
<mat-tab [disabled]="!fixedDepositsAccountProductTemplate">
<ng-template mat-tab-label>
<fa-icon icon="university" class="cfa-tab__icon"></fa-icon>
Interest Rate Chart
</ng-template>
<div class="cfa-tab-body">
@if (fixedDepositsAccountProductTemplate) {
<mifosx-fixed-deposit-account-interest-rate-chart-step
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
[cbsBaseRates]="cbsBaseRates"
(tenureSelected)="onTenureSelected($event)"
>
</mifosx-fixed-deposit-account-interest-rate-chart-step>
}
<div class="cfa-actions">
<button mat-stroked-button (click)="prev()">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<span class="cfa-actions__spacer"></span>
<button mat-raised-button color="primary" (click)="next()">
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</div>
</mat-tab>
<!-- Tab 4: Preview -->
<mat-tab [disabled]="!fixedDepositsAccountProductTemplate">
<ng-template mat-tab-label>
<fa-icon icon="eye" class="cfa-tab__icon"></fa-icon>
{{ 'labels.inputs.PREVIEW' | translate }}
</ng-template>
<div class="cfa-tab-body">
@if (fixedDepositsAccountProductTemplate) {
<mifosx-fixed-deposit-account-preview-step
[fixedDepositsAccountTemplate]="fixedDepositsAccountTemplate"
[fixedDepositsAccountProductTemplate]="fixedDepositsAccountProductTemplate"
[fixedDepositAccountData]="fixedDepositAccount"
[selectedTenure]="selectedTenure"
[fdNominees]="fixedDepositAccountCurrencyStep?.nominees ?? []"
[hideButtons]="true"
(submitEvent)="submit()"
></mifosx-fixed-deposit-account-preview-step>
}
<div class="cfa-actions">
<button mat-stroked-button (click)="prev()">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<span class="cfa-actions__spacer"></span>
<button mat-stroked-button [routerLink]="['../..']">
{{ 'labels.buttons.Cancel' | translate }}
</button>
<button mat-raised-button color="primary" class="preview-submit-row__primary" (click)="submit()">
<fa-icon icon="check" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Submit' | translate }}
</button>
</div>
</div>
</mat-tab>
</mat-tab-group>
</div> </div>
@@ -5,3 +5,166 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
$navy: #0d1b2a;
$blue: #1e88e5;
$blue-d: #1e40af;
$indigo: #4f46e5;
$border: #e2e8f0;
.cfa-container {
max-width: 1180px;
margin: 0 auto;
padding: 20px 24px;
}
/* ─── Hero header (matches Nexora pages) ────────────────────────────── */
.cfa-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
&__icon {
width: 44px;
height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background: linear-gradient(135deg, $blue 0%, $indigo 100%);
color: #fff;
font-size: 18px;
box-shadow: 0 2px 8px rgb(29 78 216 / 25%);
flex-shrink: 0;
}
&__title {
margin: 0;
font-size: 20px;
font-weight: 800;
color: $navy;
line-height: 1.1;
letter-spacing: 0.02em;
}
&__sub {
margin-top: 2px;
font-size: 12.5px;
color: #64748b;
}
}
/* ─── Wizard tab strip ──────────────────────────────────────────────── */
.cfa-tabs {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
::ng-deep {
.mat-mdc-tab-header {
background: #f8fafc;
border-bottom: 1px solid $border;
}
.mat-mdc-tab {
min-width: 140px;
height: 50px;
padding: 0 22px;
font-size: 13px !important;
font-weight: 600 !important;
letter-spacing: 0.01em;
color: #475569 !important;
opacity: 1 !important;
}
.mat-mdc-tab:hover:not(.mdc-tab--active, .mat-mdc-tab-disabled) {
background: rgb(30 64 175 / 5%);
color: $navy !important;
}
.mat-mdc-tab.mdc-tab--active {
.mdc-tab__text-label {
color: $blue-d !important;
font-weight: 700 !important;
}
}
/* Active-tab underline indicator */
.mdc-tab-indicator__content--underline {
border-color: $blue-d !important;
border-top-width: 3px !important;
}
.mat-mdc-tab.mat-mdc-tab-disabled {
opacity: 0.45 !important;
cursor: not-allowed;
}
.mat-mdc-tab-body-wrapper {
min-height: 320px;
}
.mat-mdc-tab-body,
.mat-mdc-tab-body-content {
overflow: visible !important;
}
}
}
.cfa-tab__icon {
margin-right: 8px;
font-size: 13px;
}
/* ─── Tab body + action bar ─────────────────────────────────────────── */
.cfa-tab-body {
padding: 24px;
@media (width <= 768px) {
padding: 16px;
}
}
.cfa-actions {
display: flex;
align-items: center;
gap: 12px;
margin-top: 24px;
padding-top: 16px;
border-top: 1px solid #f1f5f9;
&__spacer {
flex: 1;
}
button {
height: 40px !important;
border-radius: 10px !important;
font-weight: 600 !important;
padding: 0 18px !important;
}
}
/* Utility helpers */
.m-l-10 {
margin-left: 8px;
}
.m-r-10 {
margin-right: 8px;
}
button.preview-submit-row__primary,
.mat-mdc-raised-button.mat-primary.preview-submit-row__primary,
.preview-submit-row__primary {
background: linear-gradient(135deg, #10b981 0%, #047857 100%) !important;
--mdc-protected-button-container-color: transparent !important;
--mdc-filled-button-container-color: transparent !important;
color: #fff !important;
box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important;
}
@@ -20,14 +20,7 @@ import { ProductsService } from 'app/products/products.service';
import { FixedDepositAccountDetailsStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component'; import { FixedDepositAccountDetailsStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-details-step/fixed-deposit-account-details-step.component';
import { FixedDepositAccountCurrencyStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component'; import { FixedDepositAccountCurrencyStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-currency-step/fixed-deposit-account-currency-step.component';
import { Dates } from 'app/core/utils/dates'; import { Dates } from 'app/core/utils/dates';
import { import { MatTabsModule } from '@angular/material/tabs';
MatStepper,
MatStepperIcon,
MatStep,
MatStepLabel,
MatStepperNext,
MatStepperPrevious
} from '@angular/material/stepper';
import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { FixedDepositAccountInterestRateChartStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component'; import { FixedDepositAccountInterestRateChartStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-interest-rate-chart-step/fixed-deposit-account-interest-rate-chart-step.component';
import { FixedDepositAccountPreviewStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component'; import { FixedDepositAccountPreviewStepComponent } from '../fixed-deposit-account-stepper/fixed-deposit-account-preview-step/fixed-deposit-account-preview-step.component';
@@ -42,13 +35,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
styleUrls: ['./create-fixed-deposit-account.component.scss'], styleUrls: ['./create-fixed-deposit-account.component.scss'],
imports: [ imports: [
...STANDALONE_SHARED_IMPORTS, ...STANDALONE_SHARED_IMPORTS,
MatStepper, MatTabsModule,
MatStepperIcon,
MatStepperNext,
MatStepperPrevious,
FaIconComponent, FaIconComponent,
MatStep,
MatStepLabel,
FixedDepositAccountDetailsStepComponent, FixedDepositAccountDetailsStepComponent,
FixedDepositAccountInterestRateChartStepComponent, FixedDepositAccountInterestRateChartStepComponent,
FixedDepositAccountCurrencyStepComponent, FixedDepositAccountCurrencyStepComponent,
@@ -73,6 +61,10 @@ export class CreateFixedDepositAccountComponent {
fixedDepositsAccountTemplate: any; fixedDepositsAccountTemplate: any;
/** Fixed Deposit Account Product Template */ /** Fixed Deposit Account Product Template */
fixedDepositsAccountProductTemplate: any; fixedDepositsAccountProductTemplate: any;
/** Currently visible wizard tab (0-indexed). */
selectedTabIndex = 0;
/** Total wizard tabs — Product, Currency, Chart, Preview. */
private readonly totalTabs = 4;
/** Active CBS base interest rate versions for the selected product's FD type */ /** Active CBS base interest rate versions for the selected product's FD type */
cbsBaseRates: any[] = []; cbsBaseRates: any[] = [];
/** Tenure selected from the interest rate chart step */ /** Tenure selected from the interest rate chart step */
@@ -100,6 +92,18 @@ export class CreateFixedDepositAccountComponent {
}); });
} }
next(): void {
if (this.selectedTabIndex < this.totalTabs - 1) {
this.selectedTabIndex += 1;
}
}
prev(): void {
if (this.selectedTabIndex > 0) {
this.selectedTabIndex -= 1;
}
}
/** /**
* Sets fixed deposits account product template. * Sets fixed deposits account product template.
* @param {any} $event API response * @param {any} $event API response
@@ -73,6 +73,13 @@ export class FixedDepositAccountCurrencyStepComponent implements OnChanges {
this.nomineeError = 'This person is already added as a nominee.'; this.nomineeError = 'This person is already added as a nominee.';
return; return;
} }
const relationshipLower = data.relationshipName?.toLowerCase();
if (relationshipLower === 'father' || relationshipLower === 'mother' || relationshipLower === 'wife') {
if (this.nominees.some((n) => n.relationshipName?.toLowerCase() === relationshipLower)) {
this.nomineeError = `Only one ${data.relationshipName} can be added as a nominee.`;
return;
}
}
const newTotal = this.totalShare + (+data.percentageShare || 0); const newTotal = this.totalShare + (+data.percentageShare || 0);
if (newTotal > 100) { if (newTotal > 100) {
this.nomineeError = `Total share cannot exceed 100%. Remaining: ${this.remainingShare}%`; this.nomineeError = `Total share cannot exceed 100%. Remaining: ${this.remainingShare}%`;
@@ -867,21 +867,23 @@
</div> </div>
} }
<div class="layout-row align-center gap-2percent margin-t responsive-column"> @if (!hideButtons) {
<button mat-raised-button matStepperPrevious disabled> <div class="layout-row align-center gap-2percent margin-t responsive-column">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon> <button mat-raised-button matStepperPrevious disabled>
{{ 'labels.buttons.Previous' | translate }} <fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
</button> {{ 'labels.buttons.Previous' | translate }}
<button </button>
mat-raised-button <button
matStepperNext mat-raised-button
[disabled]=" matStepperNext
!fixedDepositAccountDetailsForm.get('productId')?.value || [disabled]="
!fixedDepositAccountDetailsForm.get('submittedOnDate')?.value !fixedDepositAccountDetailsForm.get('productId')?.value ||
" !fixedDepositAccountDetailsForm.get('submittedOnDate')?.value
> "
{{ 'labels.buttons.Next' | translate }} >
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon> {{ 'labels.buttons.Next' | translate }}
</button> <fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</div> </button>
</div>
}
</form> </form>
@@ -55,6 +55,7 @@ export class FixedDepositAccountDetailsStepComponent implements OnInit {
/** Fixed Deposits Account Template */ /** Fixed Deposits Account Template */
@Input() fixedDepositsAccountTemplate: any; @Input() fixedDepositsAccountTemplate: any;
@Input() hideButtons = false;
/** Minimum date allowed. */ /** Minimum date allowed. */
minDate = new Date(2000, 0, 1); minDate = new Date(2000, 0, 1);
@@ -22,6 +22,10 @@
</div> </div>
</div> </div>
<div class="fdrc-header-card__meta"> <div class="fdrc-header-card__meta">
<span class="fdrc-pill" [class.fdrc-pill--active]="isActive" [class.fdrc-pill--inactive]="!isActive">
<i class="fas" [class.fa-check-circle]="isActive" [class.fa-times-circle]="!isActive"></i>
{{ isActive ? 'Active' : 'Inactive' }}
</span>
@if (fdType) { @if (fdType) {
<span <span
class="fdrc-pill fdrc-pill--type" class="fdrc-pill fdrc-pill--type"
@@ -143,6 +143,18 @@ $red-light: #fee2e2;
background: $blue-light; background: $blue-light;
color: $blue; color: $blue;
} }
&--active {
background: $green-light;
color: $green;
border: 1px solid rgba($green, 0.3);
}
&--inactive {
background: $red-light;
color: $red;
border: 1px solid rgba($red, 0.3);
}
} }
// ── FD Type Rule Banner ──────────────────────────────────────────────────── // ── FD Type Rule Banner ────────────────────────────────────────────────────
@@ -74,6 +74,8 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
/** Active CBS base interest rate versions — when provided, overrides the product chart slabs */ /** Active CBS base interest rate versions — when provided, overrides the product chart slabs */
@Input() cbsBaseRates: any[] = []; @Input() cbsBaseRates: any[] = [];
activeBase: any = null;
@Output() tenureSelected = new EventEmitter<{ @Output() tenureSelected = new EventEmitter<{
depositPeriod: number; depositPeriod: number;
depositPeriodFrequencyId: number; depositPeriodFrequencyId: number;
@@ -158,9 +160,9 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
ngOnChanges() { ngOnChanges() {
if (this.fixedDepositsAccountProductTemplate) { if (this.fixedDepositsAccountProductTemplate) {
const fdType = this.fixedDepositsAccountProductTemplate?.fdType; const fdType = this.fixedDepositsAccountProductTemplate?.fdType;
const activeBase = (this.cbsBaseRates || []).find((r) => r.fdType === fdType && r.isActive); this.activeBase = (this.cbsBaseRates || []).find((r) => r.fdType === fdType && r.isActive);
this.interestRateChartData = activeBase?.slabs this.interestRateChartData = this.activeBase?.slabs
? this.normalizeCbsSlabs(activeBase.slabs) ? this.normalizeCbsSlabs(this.activeBase.slabs)
: (this.fixedDepositsAccountProductTemplate.accountChart?.chartSlabs ?? []); : (this.fixedDepositsAccountProductTemplate.accountChart?.chartSlabs ?? []);
if (this.chartsTableRef) { if (this.chartsTableRef) {
this.chartsTableRef.renderRows(); this.chartsTableRef.renderRows();
@@ -326,21 +328,41 @@ export class FixedDepositAccountInterestRateChartStepComponent implements OnChan
} }
get chartName(): string { get chartName(): string {
if (this.activeBase) {
return this.activeBase.versionLabel || 'CBS Base Interest Rate';
}
return this.fixedDepositsAccountProductTemplate?.accountChart?.name || 'CBS Base Interest Rate'; return this.fixedDepositsAccountProductTemplate?.accountChart?.name || 'CBS Base Interest Rate';
} }
get chartFromDate(): any { get chartFromDate(): any {
if (this.activeBase) {
return this.activeBase.effectiveFromDate;
}
return this.fixedDepositsAccountProductTemplate?.accountChart?.fromDate; return this.fixedDepositsAccountProductTemplate?.accountChart?.fromDate;
} }
get chartEndDate(): any { get chartEndDate(): any {
if (this.activeBase) {
return null;
}
return this.fixedDepositsAccountProductTemplate?.accountChart?.endDate; return this.fixedDepositsAccountProductTemplate?.accountChart?.endDate;
} }
get chartDescription(): string { get chartDescription(): string {
if (this.activeBase) {
return this.activeBase.remarks || '';
}
return this.fixedDepositsAccountProductTemplate?.accountChart?.description || ''; return this.fixedDepositsAccountProductTemplate?.accountChart?.description || '';
} }
get isActive(): boolean {
if (this.activeBase) {
return this.activeBase.isActive;
}
const template = this.fixedDepositsAccountProductTemplate || this.fixedDepositsAccountTemplate;
return template?.accountChart?.isActive ?? true;
}
get isPrimaryGroupingByAmount(): boolean { get isPrimaryGroupingByAmount(): boolean {
return !!this.fixedDepositsAccountProductTemplate?.accountChart?.isPrimaryGroupingByAmount; return !!this.fixedDepositsAccountProductTemplate?.accountChart?.isPrimaryGroupingByAmount;
} }
@@ -683,15 +683,18 @@
</div> </div>
</div> </div>
<div class="layout-row responsive-column align-center gap-2px margin-t"> @if (!hideButtons) {
<button mat-raised-button matStepperPrevious> <div class="layout-row responsive-column align-center gap-2px margin-t">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon> <button mat-raised-button matStepperPrevious>
{{ 'labels.buttons.Previous' | translate }} <fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
</button> {{ 'labels.buttons.Previous' | translate }}
<button mat-raised-button [routerLink]="['../..']"> </button>
{{ 'labels.buttons.Cancel' | translate }} <button mat-raised-button [routerLink]="['../..']">
</button> {{ 'labels.buttons.Cancel' | translate }}
<button mat-raised-button color="primary" (click)="submitEvent.emit()"> </button>
{{ 'labels.buttons.Submit' | translate }} <button mat-raised-button color="primary" class="preview-submit-row__primary" (click)="submitEvent.emit()">
</button> <fa-icon icon="check" class="m-r-10"></fa-icon>
</div> {{ 'labels.buttons.Submit' | translate }}
</button>
</div>
}
@@ -588,3 +588,15 @@
border-color: #bbf7d0; border-color: #bbf7d0;
} }
} }
button.preview-submit-row__primary,
.mat-mdc-raised-button.mat-primary.preview-submit-row__primary,
.preview-submit-row__primary {
background: linear-gradient(135deg, #10b981 0%, #047857 100%) !important;
--mdc-protected-button-container-color: transparent !important;
--mdc-filled-button-container-color: transparent !important;
color: #fff !important;
box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important;
}
@@ -72,6 +72,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
export class FixedDepositAccountPreviewStepComponent implements OnChanges { export class FixedDepositAccountPreviewStepComponent implements OnChanges {
/** Fixed Deposits Account Template */ /** Fixed Deposits Account Template */
@Input() fixedDepositsAccountTemplate: any; @Input() fixedDepositsAccountTemplate: any;
@Input() hideButtons = false;
/** Fixed Deposits Account Product Template */ /** Fixed Deposits Account Product Template */
@Input() fixedDepositsAccountProductTemplate: any; @Input() fixedDepositsAccountProductTemplate: any;
/** Fixed Deposits Account Data */ /** Fixed Deposits Account Data */
@@ -6,112 +6,153 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/. file, You can obtain one at http://mozilla.org/MPL/2.0/.
--> -->
<div class="container"> <div class="cra-container">
<mat-stepper <!-- Hero Header -->
orientation="vertical" <div class="cra-header">
[linear]="false" <fa-icon icon="university" class="cra-header__icon"></fa-icon>
class="mat-elevation-z8" <div>
labelPosition="bottom" <h2 class="cra-header__title">Create Recurring Deposit Account</h2>
#recurringDepositAccountStepper <div class="cra-header__sub">Pick a product, confirm currency, set terms and review before submit.</div>
</div>
</div>
<!-- Wizard tabs -->
<mat-tab-group
class="cra-tabs"
animationDuration="200ms"
[preserveContent]="true"
[(selectedIndex)]="selectedTabIndex"
> >
<ng-template matStepperIcon="number"> <!-- Tab 1: Product Classification + Details -->
<fa-icon icon="pencil-alt" size="sm"></fa-icon> <mat-tab>
</ng-template> <ng-template mat-tab-label>
<fa-icon icon="check-square" class="cra-tab__icon"></fa-icon>
Product
</ng-template>
<div class="cra-tab-body">
<mifosx-recurring-deposits-account-details-step
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
(recurringDepositsAccountProductTemplate)="setTemplate($event)"
>
</mifosx-recurring-deposits-account-details-step>
<ng-template matStepperIcon="edit"> <div class="cra-actions">
<fa-icon icon="pencil-alt" size="sm"></fa-icon> <span class="cra-actions__spacer"></span>
</ng-template> <button
mat-raised-button
<ng-template matStepperIcon="done"> color="primary"
<fa-icon icon="check" size="sm"></fa-icon> [disabled]="!recurringDepositsAccountProductTemplate"
</ng-template> (click)="next()"
>
<ng-template matStepperIcon="error">
<fa-icon icon="exclamation-triangle" size="lg"></fa-icon>
</ng-template>
<ng-template matStepperIcon="preview">
<fa-icon icon="eye" size="sm"></fa-icon>
</ng-template>
<!-- Step 1: Details -->
<mat-step>
<ng-template matStepLabel>{{ 'labels.inputs.DETAILS' | translate }}</ng-template>
<mifosx-recurring-deposits-account-details-step
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
(recurringDepositsAccountProductTemplate)="setTemplate($event)"
>
</mifosx-recurring-deposits-account-details-step>
</mat-step>
<!-- Step 2: Currency -->
@if (recurringDepositsAccountProductTemplate) {
<mat-step>
<ng-template matStepLabel>Currency</ng-template>
<mifosx-recurring-deposits-account-currency-step
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
[primaryClientId]="recurringDepositsAccountTemplate?.clientId ?? null"
></mifosx-recurring-deposits-account-currency-step>
<div class="layout-row align-center gap-2percent margin-t responsive-column">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<button mat-raised-button matStepperNext>
{{ 'labels.buttons.Next' | translate }} {{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon> <fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button> </button>
</div> </div>
</mat-step> </div>
</mat-tab>
<!-- Step 3: Interest Rate Chart --> <!-- Tab 2: Currency -->
<mat-step> <mat-tab [disabled]="!recurringDepositsAccountProductTemplate">
<ng-template matStepLabel>{{ 'labels.inputs.INTEREST RATE CHART' | translate }}</ng-template> <ng-template mat-tab-label>
<fa-icon icon="dollar-sign" class="cra-tab__icon"></fa-icon>
Currency
</ng-template>
<div class="cra-tab-body">
@if (recurringDepositsAccountProductTemplate) {
<mifosx-recurring-deposits-account-currency-step
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
[primaryClientId]="recurringDepositsAccountTemplate?.clientId ?? null"
></mifosx-recurring-deposits-account-currency-step>
}
<mifosx-recurring-deposits-account-interest-rate-chart-step <div class="cra-actions">
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate" <button mat-stroked-button (click)="prev()">
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
[submittedOnDate]="submittedOnDate"
[cbsBaseRates]="cbsBaseRates"
(tenureSelected)="handleTenureSelected($event)"
>
</mifosx-recurring-deposits-account-interest-rate-chart-step>
<div class="layout-row align-center gap-2percent margin-t responsive-column">
<button mat-raised-button matStepperPrevious>
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon> <fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }} {{ 'labels.buttons.Previous' | translate }}
</button> </button>
<button mat-raised-button matStepperNext> <span class="cra-actions__spacer"></span>
<button mat-raised-button color="primary" (click)="next()">
{{ 'labels.buttons.Next' | translate }} {{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon> <fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button> </button>
</div> </div>
</mat-step> </div>
</mat-tab>
<!-- Step 4: Preview --> <!-- Tab 3: Interest Rate Chart -->
<mat-step state="preview" completed> <mat-tab [disabled]="!recurringDepositsAccountProductTemplate">
<ng-template matStepLabel>{{ 'labels.inputs.PREVIEW' | translate }}</ng-template> <ng-template mat-tab-label>
<mifosx-recurring-deposits-account-preview-step <fa-icon icon="university" class="cra-tab__icon"></fa-icon>
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate" Interest Rate Chart
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate" </ng-template>
[recurringDepositAccountData]="recurringDepositAccountData" <div class="cra-tab-body">
[rdNominees]="recurringDepositsAccountCurrencyStep?.nominees ?? []" @if (recurringDepositsAccountProductTemplate) {
[rdCurrencyFlags]="rdCurrencyFlags" <mifosx-recurring-deposits-account-interest-rate-chart-step
[rdTenureDays]="rdTenureDays" [recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
[rdApplicableSlab]="rdApplicableSlab" [recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
[rdMaturityAmount]="rdMaturityAmount" [submittedOnDate]="submittedOnDate"
[rdInterestEarned]="rdInterestEarned" [cbsBaseRates]="cbsBaseRates"
[rdDaysInYear]="rdDaysInYear" (tenureSelected)="handleTenureSelected($event)"
[rdAppliedRate]="rdAppliedRate" >
[rdIsSeniorCitizen]="rdIsSeniorCitizen" </mifosx-recurring-deposits-account-interest-rate-chart-step>
(submitEvent)="submit()" }
>
</mifosx-recurring-deposits-account-preview-step> <div class="cra-actions">
</mat-step> <button mat-stroked-button (click)="prev()">
} <fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
</mat-stepper> {{ 'labels.buttons.Previous' | translate }}
</button>
<span class="cra-actions__spacer"></span>
<button mat-raised-button color="primary" (click)="next()">
{{ 'labels.buttons.Next' | translate }}
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
</button>
</div>
</div>
</mat-tab>
<!-- Tab 4: Preview -->
<mat-tab [disabled]="!recurringDepositsAccountProductTemplate">
<ng-template mat-tab-label>
<fa-icon icon="eye" class="cra-tab__icon"></fa-icon>
{{ 'labels.inputs.PREVIEW' | translate }}
</ng-template>
<div class="cra-tab-body">
@if (recurringDepositsAccountProductTemplate) {
<mifosx-recurring-deposits-account-preview-step
[recurringDepositsAccountTemplate]="recurringDepositsAccountTemplate"
[recurringDepositsAccountProductTemplate]="recurringDepositsAccountProductTemplate"
[recurringDepositAccountData]="recurringDepositAccountData"
[rdNominees]="recurringDepositsAccountCurrencyStep?.nominees ?? []"
[rdCurrencyFlags]="rdCurrencyFlags"
[rdTenureDays]="rdTenureDays"
[rdApplicableSlab]="rdApplicableSlab"
[rdMaturityAmount]="rdMaturityAmount"
[rdInterestEarned]="rdInterestEarned"
[rdDaysInYear]="rdDaysInYear"
[rdAppliedRate]="rdAppliedRate"
[rdIsSeniorCitizen]="rdIsSeniorCitizen"
[hideButtons]="true"
(submitEvent)="submit()"
>
</mifosx-recurring-deposits-account-preview-step>
}
<div class="cra-actions">
<button mat-stroked-button (click)="prev()">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Previous' | translate }}
</button>
<span class="cra-actions__spacer"></span>
<button mat-stroked-button [routerLink]="['../..']">
{{ 'labels.buttons.Cancel' | translate }}
</button>
<button mat-raised-button color="primary" class="preview-submit-row__primary" (click)="submit()">
<fa-icon icon="check" class="m-r-10"></fa-icon>
{{ 'labels.buttons.Submit' | translate }}
</button>
</div>
</div>
</mat-tab>
</mat-tab-group>
</div> </div>
@@ -6,14 +6,165 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
:host ::ng-deep mat-stepper { $navy: #0d1b2a;
background: transparent; $blue: #1e88e5;
$blue-d: #1e40af;
$indigo: #4f46e5;
$border: #e2e8f0;
.mat-step-content { .cra-container {
overflow: visible !important; max-width: 1180px;
margin: 0 auto;
padding: 20px 24px;
}
/* ─── Hero header (matches Nexora pages) ────────────────────────────── */
.cra-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
&__icon {
width: 44px;
height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background: linear-gradient(135deg, $blue 0%, $indigo 100%);
color: #fff;
font-size: 18px;
box-shadow: 0 2px 8px rgb(29 78 216 / 25%);
flex-shrink: 0;
}
&__title {
margin: 0;
font-size: 20px;
font-weight: 800;
color: $navy;
line-height: 1.1;
letter-spacing: 0.02em;
}
&__sub {
margin-top: 2px;
font-size: 12.5px;
color: #64748b;
} }
} }
:host ::ng-deep mat-step-content { /* ─── Wizard tab strip ──────────────────────────────────────────────── */
overflow: visible !important; .cra-tabs {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
::ng-deep {
.mat-mdc-tab-header {
background: #f8fafc;
border-bottom: 1px solid $border;
}
.mat-mdc-tab {
min-width: 140px;
height: 50px;
padding: 0 22px;
font-size: 13px !important;
font-weight: 600 !important;
letter-spacing: 0.01em;
color: #475569 !important;
opacity: 1 !important;
}
.mat-mdc-tab:hover:not(.mdc-tab--active, .mat-mdc-tab-disabled) {
background: rgb(30 64 175 / 5%);
color: $navy !important;
}
.mat-mdc-tab.mdc-tab--active {
.mdc-tab__text-label {
color: $blue-d !important;
font-weight: 700 !important;
}
}
/* Active-tab underline indicator */
.mdc-tab-indicator__content--underline {
border-color: $blue-d !important;
border-top-width: 3px !important;
}
.mat-mdc-tab.mat-mdc-tab-disabled {
opacity: 0.45 !important;
cursor: not-allowed;
}
.mat-mdc-tab-body-wrapper {
min-height: 320px;
}
.mat-mdc-tab-body,
.mat-mdc-tab-body-content {
overflow: visible !important;
}
}
}
.cra-tab__icon {
margin-right: 8px;
font-size: 13px;
}
/* ─── Tab body + action bar ─────────────────────────────────────────── */
.cra-tab-body {
padding: 24px;
@media (width <= 768px) {
padding: 16px;
}
}
.cra-actions {
display: flex;
align-items: center;
gap: 12px;
margin-top: 24px;
padding-top: 16px;
border-top: 1px solid #f1f5f9;
&__spacer {
flex: 1;
}
button {
height: 40px !important;
border-radius: 10px !important;
font-weight: 600 !important;
padding: 0 18px !important;
}
}
/* Utility helpers */
.m-l-10 {
margin-left: 8px;
}
.m-r-10 {
margin-right: 8px;
}
button.preview-submit-row__primary,
.mat-mdc-raised-button.mat-primary.preview-submit-row__primary,
.preview-submit-row__primary {
background: linear-gradient(135deg, #10b981 0%, #047857 100%) !important;
--mdc-protected-button-container-color: transparent !important;
--mdc-filled-button-container-color: transparent !important;
color: #fff !important;
box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important;
} }
@@ -19,14 +19,7 @@ import { AlertService } from 'app/core/alert/alert.service';
/** Step Components */ /** Step Components */
import { RecurringDepositsAccountDetailsStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component'; import { RecurringDepositsAccountDetailsStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-details-step/recurring-deposits-account-details-step.component';
import { Dates } from 'app/core/utils/dates'; import { Dates } from 'app/core/utils/dates';
import { import { MatTabsModule } from '@angular/material/tabs';
MatStepper,
MatStepperIcon,
MatStep,
MatStepLabel,
MatStepperNext,
MatStepperPrevious
} from '@angular/material/stepper';
import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { RecurringDepositsAccountCurrencyStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-currency-step/recurring-deposits-account-currency-step.component'; import { RecurringDepositsAccountCurrencyStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-currency-step/recurring-deposits-account-currency-step.component';
import { RecurringDepositsAccountInterestRateChartStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component'; import { RecurringDepositsAccountInterestRateChartStepComponent } from '../recurring-deposits-account-stepper/recurring-deposits-account-interest-rate-chart-step/recurring-deposits-account-interest-rate-chart-step.component';
@@ -42,13 +35,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
styleUrls: ['./create-recurring-deposits-account.component.scss'], styleUrls: ['./create-recurring-deposits-account.component.scss'],
imports: [ imports: [
...STANDALONE_SHARED_IMPORTS, ...STANDALONE_SHARED_IMPORTS,
MatStepper, MatTabsModule,
MatStepperIcon,
MatStepperNext,
MatStepperPrevious,
FaIconComponent, FaIconComponent,
MatStep,
MatStepLabel,
RecurringDepositsAccountDetailsStepComponent, RecurringDepositsAccountDetailsStepComponent,
RecurringDepositsAccountCurrencyStepComponent, RecurringDepositsAccountCurrencyStepComponent,
RecurringDepositsAccountInterestRateChartStepComponent, RecurringDepositsAccountInterestRateChartStepComponent,
@@ -77,6 +65,10 @@ export class CreateRecurringDepositsAccountComponent {
recurringDepositsAccountTemplate: any; recurringDepositsAccountTemplate: any;
/** Recurring Deposit Account Product Template */ /** Recurring Deposit Account Product Template */
recurringDepositsAccountProductTemplate: any; recurringDepositsAccountProductTemplate: any;
/** Currently visible wizard tab (0-indexed). */
selectedTabIndex = 0;
/** Total wizard tabs — Product, Currency, Chart, Preview. */
private readonly totalTabs = 4;
/** Active CBS base interest rate versions */ /** Active CBS base interest rate versions */
cbsBaseRates: any[] = []; cbsBaseRates: any[] = [];
/** Selected tenure value and frequency from calculator */ /** Selected tenure value and frequency from calculator */
@@ -92,6 +84,18 @@ export class CreateRecurringDepositsAccountComponent {
}); });
} }
next(): void {
if (this.selectedTabIndex < this.totalTabs - 1) {
this.selectedTabIndex += 1;
}
}
prev(): void {
if (this.selectedTabIndex > 0) {
this.selectedTabIndex -= 1;
}
}
/** /**
* Sets recurring deposits account product template. * Sets recurring deposits account product template.
* Also fetches the active CBS base interest rate chart. * Also fetches the active CBS base interest rate chart.
@@ -71,6 +71,13 @@ export class RecurringDepositsAccountCurrencyStepComponent implements OnChanges
this.nomineeError = 'This person is already added as a nominee.'; this.nomineeError = 'This person is already added as a nominee.';
return; return;
} }
const relationshipLower = data.relationshipName?.toLowerCase();
if (relationshipLower === 'father' || relationshipLower === 'mother' || relationshipLower === 'wife') {
if (this.nominees.some((n) => n.relationshipName?.toLowerCase() === relationshipLower)) {
this.nomineeError = `Only one ${data.relationshipName} can be added as a nominee.`;
return;
}
}
const newTotal = this.totalShare + (+data.percentageShare || 0); const newTotal = this.totalShare + (+data.percentageShare || 0);
if (newTotal > 100) { if (newTotal > 100) {
this.nomineeError = `Total share cannot exceed 100%. Remaining: ${this.remainingShare}%`; this.nomineeError = `Total share cannot exceed 100%. Remaining: ${this.remainingShare}%`;
@@ -22,6 +22,10 @@
</div> </div>
</div> </div>
<div class="fdrc-header-card__meta"> <div class="fdrc-header-card__meta">
<span class="fdrc-pill" [class.fdrc-pill--active]="isActive" [class.fdrc-pill--inactive]="!isActive">
<i class="fas" [class.fa-check-circle]="isActive" [class.fa-times-circle]="!isActive"></i>
{{ isActive ? 'Active' : 'Inactive' }}
</span>
<div class="fdrc-header-card__dates"> <div class="fdrc-header-card__dates">
<span class="fdrc-meta-item"> <span class="fdrc-meta-item">
<i class="fas fa-calendar-check"></i> <i class="fas fa-calendar-check"></i>
@@ -143,6 +143,18 @@ $red-light: #fee2e2;
background: $blue-light; background: $blue-light;
color: $blue; color: $blue;
} }
&--active {
background: $green-light;
color: $green;
border: 1px solid rgba($green, 0.3);
}
&--inactive {
background: $red-light;
color: $red;
border: 1px solid rgba($red, 0.3);
}
} }
// ── Calculator Card ──────────────────────────────────────────────────────── // ── Calculator Card ────────────────────────────────────────────────────────
@@ -98,10 +98,13 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
isSeniorCitizen?: boolean; isSeniorCitizen?: boolean;
}>(); }>();
get activeBase(): any {
return (this.cbsBaseRates || []).find((r) => r.fdType === 'DOUBLE' && r.isActive);
}
get interestRateChartData(): any[] { get interestRateChartData(): any[] {
const activeBase = (this.cbsBaseRates || []).find((r) => r.fdType === 'DOUBLE' && r.isActive); if (this.activeBase?.slabs?.length) {
if (activeBase?.slabs?.length) { return this.normalizeCbsSlabs(this.activeBase.slabs);
return this.normalizeCbsSlabs(activeBase.slabs);
} }
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate; const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
if (!template) return []; if (!template) return [];
@@ -336,6 +339,9 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
// ── Header Getters ────────────────────────────────────────────────────── // ── Header Getters ──────────────────────────────────────────────────────
get chartName(): string { get chartName(): string {
if (this.activeBase) {
return this.activeBase.versionLabel || 'CBS Base Interest Rate';
}
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate; const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
return ( return (
template?.accountChart?.name || template?.accountChart?.name ||
@@ -348,6 +354,9 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
} }
get chartFromDate(): any { get chartFromDate(): any {
if (this.activeBase) {
return this.activeBase.effectiveFromDate;
}
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate; const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
return ( return (
template?.accountChart?.fromDate || template?.accountChart?.fromDate ||
@@ -359,6 +368,9 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
} }
get chartEndDate(): any { get chartEndDate(): any {
if (this.activeBase) {
return null;
}
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate; const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
return ( return (
template?.accountChart?.endDate || template?.accountChart?.endDate ||
@@ -370,6 +382,9 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
} }
get chartDescription(): string { get chartDescription(): string {
if (this.activeBase) {
return this.activeBase.remarks || '';
}
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate; const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
return ( return (
template?.accountChart?.description || template?.accountChart?.description ||
@@ -381,6 +396,20 @@ export class RecurringDepositsAccountInterestRateChartStepComponent implements O
); );
} }
get isActive(): boolean {
if (this.activeBase) {
return this.activeBase.isActive;
}
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
return (
template?.accountChart?.isActive ??
template?.activeChart?.isActive ??
template?.interestRateCharts?.[0]?.isActive ??
template?.interestRateChart?.isActive ??
true
);
}
get isPrimaryGroupingByAmount(): boolean { get isPrimaryGroupingByAmount(): boolean {
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate; const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
return !!( return !!(
@@ -853,15 +853,18 @@
} }
</div> </div>
<div class="layout-row responsive-column align-center gap-2px margin-t"> @if (!hideButtons) {
<button mat-raised-button matStepperPrevious> <div class="layout-row responsive-column align-center gap-2px margin-t">
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon> <button mat-raised-button matStepperPrevious>
{{ 'labels.buttons.Previous' | translate }} <fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
</button> {{ 'labels.buttons.Previous' | translate }}
<button mat-raised-button [routerLink]="['../..']"> </button>
{{ 'labels.buttons.Cancel' | translate }} <button mat-raised-button [routerLink]="['../..']">
</button> {{ 'labels.buttons.Cancel' | translate }}
<button mat-raised-button color="primary" (click)="submitEvent.emit()"> </button>
{{ 'labels.buttons.Submit' | translate }} <button mat-raised-button color="primary" class="preview-submit-row__primary" (click)="submitEvent.emit()">
</button> <fa-icon icon="check" class="m-r-10"></fa-icon>
</div> {{ 'labels.buttons.Submit' | translate }}
</button>
</div>
}
@@ -630,3 +630,15 @@
} }
} }
} }
button.preview-submit-row__primary,
.mat-mdc-raised-button.mat-primary.preview-submit-row__primary,
.preview-submit-row__primary {
background: linear-gradient(135deg, #10b981 0%, #047857 100%) !important;
--mdc-protected-button-container-color: transparent !important;
--mdc-filled-button-container-color: transparent !important;
color: #fff !important;
box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important;
}
@@ -71,6 +71,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
export class RecurringDepositsAccountPreviewStepComponent implements OnChanges { export class RecurringDepositsAccountPreviewStepComponent implements OnChanges {
/** Input Data */ /** Input Data */
@Input() recurringDepositsAccountTemplate: any; @Input() recurringDepositsAccountTemplate: any;
@Input() hideButtons = false;
@Input() recurringDepositsAccountProductTemplate: any; @Input() recurringDepositsAccountProductTemplate: any;
@Input() recurringDepositAccountData: any; @Input() recurringDepositAccountData: any;
@Input() rdNominees: any[] = []; @Input() rdNominees: any[] = [];
@@ -58,6 +58,13 @@ export class SavingsAccountCurrencyStepComponent {
this.nomineeError = 'This person is already added as a nominee.'; this.nomineeError = 'This person is already added as a nominee.';
return; return;
} }
const relationshipLower = data.relationshipName?.toLowerCase();
if (relationshipLower === 'father' || relationshipLower === 'mother' || relationshipLower === 'wife') {
if (this.nominees.some((n) => n.relationshipName?.toLowerCase() === relationshipLower)) {
this.nomineeError = `Only one ${data.relationshipName} can be added as a nominee.`;
return;
}
}
const newTotal = this.totalShare + (+data.percentageShare || 0); const newTotal = this.totalShare + (+data.percentageShare || 0);
if (newTotal > 100) { if (newTotal > 100) {
this.nomineeError = `Total share cannot exceed 100%. Remaining: ${this.remainingShare}%`; this.nomineeError = `Total share cannot exceed 100%. Remaining: ${this.remainingShare}%`;
@@ -33,6 +33,10 @@
&__primary { &__primary {
background: linear-gradient(135deg, #10b981 0%, #047857 100%) !important; background: linear-gradient(135deg, #10b981 0%, #047857 100%) !important;
--mdc-protected-button-container-color: transparent !important;
--mdc-filled-button-container-color: transparent !important;
color: #fff !important; color: #fff !important;
box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important; box-shadow: 0 4px 12px -2px rgb(4 120 87 / 35%) !important;
} }
+2 -2
View File
@@ -9,8 +9,8 @@
/* tslint:disable */ /* tslint:disable */
export default { export default {
'mifos_x': { 'mifos_x': {
'version': '260521', 'version': '260522',
'hash': '846b2484' 'hash': '64c76a98'
}, },
'allow_switching_backend_instance': true 'allow_switching_backend_instance': true
}; };