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>
272 lines
8.9 KiB
TypeScript
272 lines
8.9 KiB
TypeScript
/**
|
|
* Copyright since 2025 Mifos Initiative
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* 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/.
|
|
*/
|
|
|
|
/** Angular Imports */
|
|
import { Component, Input, Output, EventEmitter, OnChanges } from '@angular/core';
|
|
import { animate, state, style, transition, trigger } from '@angular/animations';
|
|
import { MatDivider } from '@angular/material/divider';
|
|
import { NgIf, NgSwitch, TitleCasePipe } from '@angular/common';
|
|
import {
|
|
MatTable,
|
|
MatColumnDef,
|
|
MatHeaderCellDef,
|
|
MatHeaderCell,
|
|
MatCellDef,
|
|
MatCell,
|
|
MatHeaderRowDef,
|
|
MatHeaderRow,
|
|
MatRowDef,
|
|
MatRow
|
|
} from '@angular/material/table';
|
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
|
import { MatStepperPrevious } from '@angular/material/stepper';
|
|
import { FindPipe } from '../../../../pipes/find.pipe';
|
|
import { DateFormatPipe } from '../../../../pipes/date-format.pipe';
|
|
import { FormatNumberPipe } from '../../../../pipes/format-number.pipe';
|
|
import { YesnoPipe } from '../../../../pipes/yesno.pipe';
|
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|
|
|
/**
|
|
* Recurring Deposit Preview Step
|
|
*/
|
|
@Component({
|
|
selector: 'mifosx-recurring-deposits-account-preview-step',
|
|
templateUrl: './recurring-deposits-account-preview-step.component.html',
|
|
styleUrls: ['./recurring-deposits-account-preview-step.component.scss'],
|
|
animations: [
|
|
trigger('expandChartSlab', [
|
|
state('collapsed', style({ height: '0px', minHeight: '0' })),
|
|
state('expanded', style({ height: '*' })),
|
|
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)'))
|
|
])
|
|
],
|
|
imports: [
|
|
...STANDALONE_SHARED_IMPORTS,
|
|
MatDivider,
|
|
MatTable,
|
|
MatColumnDef,
|
|
MatHeaderCellDef,
|
|
MatHeaderCell,
|
|
MatCellDef,
|
|
MatCell,
|
|
FaIconComponent,
|
|
NgSwitch,
|
|
MatHeaderRowDef,
|
|
MatHeaderRow,
|
|
MatRowDef,
|
|
MatRow,
|
|
MatStepperPrevious,
|
|
TitleCasePipe,
|
|
FindPipe,
|
|
DateFormatPipe,
|
|
FormatNumberPipe,
|
|
YesnoPipe
|
|
]
|
|
})
|
|
export class RecurringDepositsAccountPreviewStepComponent implements OnChanges {
|
|
/** Input Data */
|
|
@Input() recurringDepositsAccountTemplate: any;
|
|
@Input() hideButtons = false;
|
|
@Input() recurringDepositsAccountProductTemplate: any;
|
|
@Input() recurringDepositAccountData: any;
|
|
@Input() rdNominees: any[] = [];
|
|
@Input() rdCurrencyFlags: any = null;
|
|
@Input() rdTenureDays: number | null = null;
|
|
@Input() rdApplicableSlab: any = null;
|
|
@Input() rdMaturityAmount: number | null = null;
|
|
@Input() rdInterestEarned: number | null = null;
|
|
@Input() rdDaysInYear: number | null = null;
|
|
@Input() rdAppliedRate: number | null = null;
|
|
@Input() rdIsSeniorCitizen = false;
|
|
|
|
get rdAppliedInterestRate(): number {
|
|
if (this.rdAppliedRate != null) return this.rdAppliedRate;
|
|
return this.rdApplicableSlab?.annualInterestRate ?? 0;
|
|
}
|
|
|
|
get rdSeniorCitizenBonus(): number {
|
|
if (!this.rdIsSeniorCitizen || this.rdAppliedRate == null) return 0;
|
|
const normalRate = this.rdApplicableSlab?.annualInterestRate ?? 0;
|
|
return this.rdAppliedRate - normalRate;
|
|
}
|
|
|
|
/** Output the submit action */
|
|
@Output() submitEvent = new EventEmitter();
|
|
|
|
get currencyName(): string {
|
|
return this.recurringDepositsAccountProductTemplate?.currency?.name || '—';
|
|
}
|
|
|
|
get decimalPlaces(): string | number {
|
|
return this.recurringDepositsAccountProductTemplate?.currency?.decimalPlaces ?? '—';
|
|
}
|
|
|
|
get inMultiplesOf(): string | number {
|
|
return this.recurringDepositsAccountProductTemplate?.currency?.inMultiplesOf ?? '—';
|
|
}
|
|
|
|
readonly rdSlabColumns = [
|
|
'srNo',
|
|
'description',
|
|
'fromPeriod',
|
|
'toPeriod',
|
|
'periodTypeLabel',
|
|
'annualInterestRate'
|
|
];
|
|
|
|
/** Charges Displayed Columns */
|
|
chargesDisplayedColumns: string[] = [
|
|
'name',
|
|
'chargeCalculationType',
|
|
'amount',
|
|
'chargeTimeType',
|
|
'date',
|
|
'repaymentsEvery'
|
|
];
|
|
get interestRateChartData(): any[] {
|
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
|
if (!template) return [];
|
|
return (
|
|
template.accountChart?.chartSlabs ??
|
|
template.activeChart?.chartSlabs ??
|
|
template.interestRateCharts?.[0]?.chartSlabs ??
|
|
template.interestRateChart?.chartSlabs ??
|
|
template.chartSlabs ??
|
|
[]
|
|
);
|
|
}
|
|
|
|
/** Matched Slab Index for highlighting */
|
|
get matchedSlabIndex(): number {
|
|
if (!this.rdTenureDays || !this.interestRateChartData) return -1;
|
|
|
|
// Find index of the applicable slab in the chart data
|
|
return this.interestRateChartData.findIndex((slab) => {
|
|
const pTypeValue = slab.periodType?.value || 'Days';
|
|
const fromDays = this.slabPeriodToDays(slab.fromPeriod ?? 0, pTypeValue);
|
|
const toDays = slab.toPeriod != null ? this.slabPeriodToDays(slab.toPeriod, pTypeValue) : Infinity;
|
|
return this.rdTenureDays >= fromDays && this.rdTenureDays <= toDays;
|
|
});
|
|
}
|
|
|
|
/** Selected Tenure Label for banner */
|
|
get selectedTenureLabel(): string {
|
|
if (!this.rdTenureDays) return '';
|
|
if (this.rdTenureDays >= 365) {
|
|
const years = Math.floor(this.rdTenureDays / 365);
|
|
const rem = this.rdTenureDays % 365;
|
|
return years + (years === 1 ? ' Year' : ' Years') + (rem > 0 ? ` ${rem} Days` : '');
|
|
}
|
|
if (this.rdTenureDays >= 30) {
|
|
const months = Math.floor(this.rdTenureDays / 30);
|
|
const rem = this.rdTenureDays % 30;
|
|
return months + (months === 1 ? ' Month' : ' Months') + (rem > 0 ? ` ${rem} Days` : '');
|
|
}
|
|
return this.rdTenureDays + ' Days';
|
|
}
|
|
|
|
private slabPeriodToDays(period: number, periodTypeValue: string): number {
|
|
const p = (periodTypeValue || '').toLowerCase();
|
|
if (p.includes('day')) return period;
|
|
if (p.includes('week')) return period * 7;
|
|
if (p.includes('month')) return period * 30;
|
|
if (p.includes('year')) return period * 365;
|
|
return period;
|
|
}
|
|
|
|
/** Columns to be displayed in interest rate chart table. */
|
|
chartSlabsDisplayedColumns: any[] = [
|
|
'period',
|
|
'amountRange',
|
|
'interest',
|
|
'description',
|
|
'actions'
|
|
];
|
|
/** Columns to be displayed in incentives sub-table. */
|
|
incentivesDisplayedColumns: string[] = [
|
|
'entityType',
|
|
'attributeName',
|
|
'conditionType',
|
|
'attributeValue',
|
|
'incentiveType',
|
|
'amount'
|
|
];
|
|
/** Additional Column to disblac incentives table */
|
|
chartSlabsIncentivesDisplayedColumns: string[] = ['incentives'];
|
|
/** Expand Chart Slab Index used in the view */
|
|
expandChartSlabIndex: number;
|
|
|
|
constructor() {}
|
|
|
|
ngOnChanges() {
|
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
|
if (template) {
|
|
// Logic for metadata getters is now handled by the dynamic template reference
|
|
}
|
|
}
|
|
|
|
// ── Header Getters ──────────────────────────────────────────────────────
|
|
|
|
get chartName(): string {
|
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
|
return (
|
|
template?.accountChart?.name ||
|
|
template?.activeChart?.name ||
|
|
template?.interestRateCharts?.[0]?.name ||
|
|
template?.interestRateChart?.name ||
|
|
template?.name ||
|
|
''
|
|
);
|
|
}
|
|
|
|
get chartFromDate(): any {
|
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
|
return (
|
|
template?.accountChart?.fromDate ||
|
|
template?.activeChart?.fromDate ||
|
|
template?.interestRateCharts?.[0]?.fromDate ||
|
|
template?.interestRateChart?.fromDate ||
|
|
template?.fromDate
|
|
);
|
|
}
|
|
|
|
get chartEndDate(): any {
|
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
|
return (
|
|
template?.accountChart?.endDate ||
|
|
template?.activeChart?.endDate ||
|
|
template?.interestRateCharts?.[0]?.endDate ||
|
|
template?.interestRateChart?.endDate ||
|
|
template?.endDate
|
|
);
|
|
}
|
|
|
|
get chartDescription(): string {
|
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
|
return (
|
|
template?.accountChart?.description ||
|
|
template?.activeChart?.description ||
|
|
template?.interestRateCharts?.[0]?.description ||
|
|
template?.interestRateChart?.description ||
|
|
template?.description ||
|
|
''
|
|
);
|
|
}
|
|
|
|
get isPrimaryGroupingByAmount(): boolean {
|
|
const template = this.recurringDepositsAccountProductTemplate || this.recurringDepositsAccountTemplate;
|
|
return !!(
|
|
template?.accountChart?.isPrimaryGroupingByAmount ||
|
|
template?.activeChart?.isPrimaryGroupingByAmount ||
|
|
template?.interestRateCharts?.[0]?.isPrimaryGroupingByAmount ||
|
|
template?.interestRateChart?.isPrimaryGroupingByAmount ||
|
|
template?.isPrimaryGroupingByAmount
|
|
);
|
|
}
|
|
}
|