feat: KYC re-verify UX, family/nominee tab gating, savings dialogs, cfcfrms shell
- KYC tab: fix Verify (PAN) and Send OTP (Aadhaar) being disabled in edit mode; unmask Aadhaar input and show on-file masked value as hint; resolve kycId from id/kycId/resourceId fallbacks. - Client stepper/view: add familyAllowed / nomineeAllowed predicates + CanActivate route guards. Family tab hidden for non-individuals, Nominee tab hidden for non-proprietor entities. Family-members step accepts a nomineeOnly input for proprietorship clients. Add/Edit/Delete buttons inside family-members and nominee tabs gated by canAdd. - cfcfrms: add cfcfrms-shell wrapper, restyle complaint/fraud/mule/escalate pages and dashboard. - Savings: identity-verify and transaction-confirm dialogs, manage / activate / add-charge flows, account preview styling. - Tasks, core shell, account-nominees, icons: minor style and behaviour updates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
bf3e26a0d0
commit
b149fef72e
@@ -13,6 +13,7 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
/** Custom Dialogs */
|
||||
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
||||
import { SuccessDialogComponent } from 'app/shared/success-dialog/success-dialog.component';
|
||||
import { CalculateInterestDialogComponent } from './custom-dialogs/calculate-interest-dialog/calculate-interest-dialog.component';
|
||||
import { PostInterestDialogComponent } from './custom-dialogs/post-interest-dialog/post-interest-dialog.component';
|
||||
import { ToggleWithholdTaxDialogComponent } from './custom-dialogs/toggle-withhold-tax-dialog/toggle-withhold-tax-dialog.component';
|
||||
@@ -26,14 +27,6 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/** Environment Configuration */
|
||||
import { environment } from '../../../environments/environment';
|
||||
import {
|
||||
MatCard,
|
||||
MatCardHeader,
|
||||
MatCardTitleGroup,
|
||||
MatCardMdImage,
|
||||
MatCardTitle,
|
||||
MatCardContent
|
||||
} from '@angular/material/card';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { NgClass, CurrencyPipe } from '@angular/common';
|
||||
import { LongTextComponent } from '../../shared/long-text/long-text.component';
|
||||
@@ -48,6 +41,9 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/**
|
||||
* Savings Account View Component
|
||||
*
|
||||
* Nexora redesign: gradient hero card with balance KPIs + pill-style tab
|
||||
* navigation. No more `mat-card` chrome — pure custom layout.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-savings-account-view',
|
||||
@@ -55,11 +51,7 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
styleUrls: ['./savings-account-view.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatCardHeader,
|
||||
MatCardTitleGroup,
|
||||
MatCardMdImage,
|
||||
MatTooltip,
|
||||
MatCardTitle,
|
||||
NgClass,
|
||||
LongTextComponent,
|
||||
AccountNumberComponent,
|
||||
@@ -97,6 +89,87 @@ export class SavingsAccountViewComponent implements OnInit {
|
||||
isActive = false;
|
||||
currency: Currency;
|
||||
|
||||
/**
|
||||
* Categorisation map used by the action menu — every action name is
|
||||
* bucketed into one of: 'transact' (cash movement), 'block' (restrict
|
||||
* access), 'interest' (interest postings), 'admin' (approvals, charges,
|
||||
* staff), 'danger' (close / reject / delete). Drives the section split
|
||||
* and the colour of each icon.
|
||||
*/
|
||||
private readonly ACTION_KIND: Record<string, 'transact' | 'block' | 'interest' | 'admin' | 'danger'> = {
|
||||
Deposit: 'transact',
|
||||
Withdrawal: 'transact',
|
||||
'Hold Amount': 'transact',
|
||||
'Transfer Funds': 'transact',
|
||||
'Interbank Transfer': 'transact',
|
||||
'Block Deposit': 'block',
|
||||
'Unblock Deposit': 'block',
|
||||
'Block Withdrawal': 'block',
|
||||
'Unblock Withdrawal': 'block',
|
||||
'Block Account': 'block',
|
||||
'Unblock Account': 'block',
|
||||
'Calculate Interest': 'interest',
|
||||
'Post Interest': 'interest',
|
||||
'Post Interest As On': 'interest',
|
||||
'Enable Withhold Tax': 'interest',
|
||||
'Disable Withhold Tax': 'interest',
|
||||
'Add Charge': 'admin',
|
||||
'Apply Annual Fees': 'admin',
|
||||
'Modify Application': 'admin',
|
||||
'Assign Staff': 'admin',
|
||||
'Unassign Staff': 'admin',
|
||||
Approve: 'admin',
|
||||
Activate: 'admin',
|
||||
'Undo Approval': 'admin',
|
||||
Close: 'danger',
|
||||
Reject: 'danger',
|
||||
Delete: 'danger',
|
||||
'Withdrawn by Client': 'danger'
|
||||
};
|
||||
|
||||
/** Lightweight icon fallback for actions whose buttonConfig doesn't
|
||||
* carry an icon (the `options` sub-menu items historically didn't). */
|
||||
private readonly ACTION_ICON: Record<string, string> = {
|
||||
Deposit: 'arrow-down',
|
||||
Withdrawal: 'arrow-up',
|
||||
'Hold Amount': 'pause',
|
||||
'Transfer Funds': 'exchange-alt',
|
||||
'Interbank Transfer': 'random',
|
||||
'Block Deposit': 'lock',
|
||||
'Unblock Deposit': 'lock-open',
|
||||
'Block Withdrawal': 'lock',
|
||||
'Unblock Withdrawal': 'lock-open',
|
||||
'Block Account': 'lock',
|
||||
'Unblock Account': 'lock-open',
|
||||
'Calculate Interest': 'calculator',
|
||||
'Post Interest': 'percentage',
|
||||
'Post Interest As On': 'calendar-day',
|
||||
'Enable Withhold Tax': 'percentage',
|
||||
'Disable Withhold Tax': 'percentage',
|
||||
'Add Charge': 'plus-circle',
|
||||
'Apply Annual Fees': 'receipt',
|
||||
'Modify Application': 'edit',
|
||||
'Assign Staff': 'user-plus',
|
||||
'Unassign Staff': 'user-slash',
|
||||
Approve: 'check-circle',
|
||||
Activate: 'check',
|
||||
'Undo Approval': 'undo',
|
||||
Close: 'times-circle',
|
||||
Reject: 'ban',
|
||||
Delete: 'trash',
|
||||
'Withdrawn by Client': 'user-slash'
|
||||
};
|
||||
|
||||
/** Returns the colour kind for a given action name (used to tint the
|
||||
* icon and hover state in the dropdown). */
|
||||
kindOf(name: string): 'transact' | 'block' | 'interest' | 'admin' | 'danger' {
|
||||
return this.ACTION_KIND[name] ?? 'admin';
|
||||
}
|
||||
|
||||
iconOf(name: string, fallback?: string): string {
|
||||
return this.ACTION_ICON[name] ?? fallback ?? 'circle';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches savings account data from `resolve`
|
||||
* @param {ActivatedRoute} route Activated Route
|
||||
@@ -373,9 +446,33 @@ export class SavingsAccountViewComponent implements OnInit {
|
||||
unblockSavingsAccountDialogRef.afterClosed().subscribe((response: { confirm: any }) => {
|
||||
if (response.confirm) {
|
||||
this.savingsService.executeSavingsAccountCommand(this.savingsAccountData.id, command, {}).subscribe(() => {
|
||||
this.reload();
|
||||
this.showUnblockSuccess(action);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the shared success dialog with copy tuned to the unblock variant
|
||||
* the cashier just performed, then reloads the savings view so the
|
||||
* account substatus shows as unblocked.
|
||||
*/
|
||||
private showUnblockSuccess(action: string): void {
|
||||
let title = 'Account Unblocked Successfully';
|
||||
let message = 'Account activity has been restored.';
|
||||
if (action === 'Unblock Deposit') {
|
||||
title = 'Deposits Unblocked Successfully';
|
||||
message = 'Incoming credits to this savings account are accepted again.';
|
||||
} else if (action === 'Unblock Withdrawal') {
|
||||
title = 'Withdrawals Unblocked Successfully';
|
||||
message = 'Outgoing debits from this savings account are accepted again.';
|
||||
}
|
||||
const ref = this.dialog.open(SuccessDialogComponent, {
|
||||
width: '420px',
|
||||
maxWidth: '92vw',
|
||||
autoFocus: false,
|
||||
data: { title, message, buttonLabel: 'Done' }
|
||||
});
|
||||
ref.afterClosed().subscribe(() => this.reload());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user