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:
Aditya
2026-05-21 18:49:43 +05:30
co-authored by Claude Opus 4.7
parent bf3e26a0d0
commit b149fef72e
99 changed files with 8575 additions and 2574 deletions
@@ -6,23 +6,22 @@
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<div class="container">
<div class="mat-elevation-z1 m-b-25" [hidden]="!accountWithTransactions">
<div class="layout-row align-end">
@if (checkStatus()) {
<div class="action-button m-b-20 layout-row gap-20px">
<mat-checkbox [formControl]="hideReversedParam" (click)="hideReversed()" class="accruals">
{{ 'labels.inputs.Hide Reversed' | translate }}</mat-checkbox
>
<mat-checkbox [formControl]="hideAccrualsParam" (click)="hideAccruals()" class="accruals">
{{ 'labels.inputs.Hide Accruals' | translate }}</mat-checkbox
>
<button mat-raised-button color="primary" [routerLink]="['export']">
{{ 'labels.buttons.Export' | translate }}
</button>
</div>
}
</div>
<div class="txn-tab">
<div class="txn-tab__card" [hidden]="!accountWithTransactions">
@if (checkStatus()) {
<div class="txn-tab__toolbar">
<mat-checkbox [formControl]="hideReversedParam" (click)="hideReversed()" class="txn-tab__check">
{{ 'labels.inputs.Hide Reversed' | translate }}
</mat-checkbox>
<mat-checkbox [formControl]="hideAccrualsParam" (click)="hideAccruals()" class="txn-tab__check">
{{ 'labels.inputs.Hide Accruals' | translate }}
</mat-checkbox>
<span class="txn-tab__spacer"></span>
<button mat-flat-button class="txn-tab__export" [routerLink]="['export']">
{{ 'labels.buttons.Export' | translate }}
</button>
</div>
}
<table mat-table [dataSource]="dataSource" matSort>
<ng-container matColumnDef="row">
@@ -59,13 +58,6 @@
</td>
</ng-container>
<ng-container matColumnDef="externalId">
<th mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'labels.inputs.External Id' | translate }}</th>
<td mat-cell *matCellDef="let transaction" [ngClass]="savingsTransactionColor(transaction)">
<mifosx-external-identifier externalId="{{ transaction.externalId }}"></mifosx-external-identifier>
</td>
</ng-container>
<ng-container matColumnDef="transactionType">
<th mat-header-cell class="center" *matHeaderCellDef>{{ 'labels.inputs.Transaction Type' | translate }}</th>
<td
@@ -176,10 +168,11 @@
<mat-paginator [pageSize]="50" [pageSizeOptions]="[50, 100, 200]" showFirstLastButtons></mat-paginator>
</div>
<div class="alert" [hidden]="accountWithTransactions">
<div class="message">
<i class="fa fa-exclamation-circle alert-check"></i>
<div class="txn-tab__empty" [hidden]="accountWithTransactions">
<fa-icon icon="inbox" class="txn-tab__empty-ico"></fa-icon>
<div class="txn-tab__empty-title">
{{ 'labels.text.No transaction was found' | translate }}
</div>
<div class="txn-tab__empty-sub">Posted transactions will appear here.</div>
</div>
</div>
@@ -6,37 +6,153 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
@use 'assets/styles/helper';
$navy: #0d1b2a;
$slate: #475569;
$muted: #64748b;
$border: #e2e8f0;
$soft: #f8fafc;
$blue: #1e40af;
.tab-container {
padding: 1%;
margin: 1%;
.txn-tab {
/* Wrapper hero (savings-account-view) already provides the page padding;
this tab sits flush so the table can use the full width. */
width: 100%;
}
h3 {
margin: 1% auto;
.txn-tab__card {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
overflow: hidden;
}
/* ─── Toolbar ─────────────────────────────────────────────────────── */
.txn-tab__toolbar {
display: flex;
align-items: center;
gap: 18px;
padding: 12px 18px;
background: $soft;
border-bottom: 1px solid $border;
flex-wrap: wrap;
}
.txn-tab__check {
font-size: 13px;
color: $slate;
font-weight: 500;
}
.txn-tab__spacer {
flex: 1;
}
.txn-tab__export {
height: 36px !important;
border-radius: 8px !important;
font-weight: 600 !important;
font-size: 13px !important;
padding: 0 16px !important;
background: linear-gradient(135deg, #1e88e5 0%, $blue 100%) !important;
color: #fff !important;
}
/* ─── Table ───────────────────────────────────────────────────────── */
table {
width: 100%;
}
::ng-deep .txn-tab__card {
.mat-mdc-header-row {
background: $soft;
}
.action-button {
margin-left: auto;
.mat-mdc-header-cell {
font-size: 11.5px;
font-weight: 800;
color: $slate;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 12px 14px;
border-bottom: 1px solid $border;
}
table {
width: 100%;
.mat-mdc-cell {
font-size: 13px;
color: $navy;
padding: 11px 14px;
border-bottom: 1px solid #f1f5f9;
}
.account-action-button {
min-width: 26px;
padding: 0 0 3px;
margin: 0 10%;
line-height: 25px;
.mat-mdc-row:hover {
background: #f8fafc;
cursor: pointer;
}
.accruals {
padding-top: 1%;
}
}
/* Tabular numerics on the money columns (Debit / Credit / Balance) +
right-align them. */
.mat-column-debit,
.mat-column-credit,
.mat-column-balance {
text-align: right;
font-variant-numeric: tabular-nums;
}
.mat-column-actions {
text-align: center;
width: 80px;
}
/* Compact paginator */
.mat-mdc-paginator {
border-top: 1px solid $border;
background: $soft;
min-height: 48px;
}
/* Row tint variants — strike-through reversed, dim accrual, blue transfer */
.strike td {
text-decoration: line-through;
color: $muted;
}
.accrual td {
color: $muted;
font-style: italic;
}
.transfer td {
color: #6d28d9;
}
}
.select-row:hover {
cursor: pointer;
font-weight: 800;
/* ─── Empty state ─────────────────────────────────────────────────── */
.txn-tab__empty {
background: #fff;
border: 1px solid $border;
border-radius: 14px;
box-shadow: 0 1px 3px rgb(15 23 42 / 4%);
padding: 56px 24px;
text-align: center;
color: $muted;
}
.txn-tab__empty-ico {
font-size: 34px;
color: #cbd5e1;
margin-bottom: 8px;
}
.txn-tab__empty-title {
font-size: 14.5px;
font-weight: 700;
color: $slate;
margin-top: 6px;
}
.txn-tab__empty-sub {
font-size: 12.5px;
color: $muted;
margin-top: 2px;
}
@@ -38,7 +38,6 @@ import { MatDialog } from '@angular/material/dialog';
import { Currency } from 'app/shared/models/general.model';
import { MatCheckbox } from '@angular/material/checkbox';
import { MatButton, MatIconButton } from '@angular/material/button';
import { ExternalIdentifierComponent } from '../../../shared/external-identifier/external-identifier.component';
import { MatMenuTrigger, MatMenu, MatMenuItem } from '@angular/material/menu';
import { MatIcon } from '@angular/material/icon';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
@@ -64,7 +63,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
MatCellDef,
MatCell,
NgClass,
ExternalIdentifierComponent,
MatIconButton,
MatMenuTrigger,
MatIcon,
@@ -102,7 +100,6 @@ export class TransactionsTabComponent implements OnInit {
'row',
'id',
'date',
'externalId',
'transactionType',
'debit',
'credit',