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
@@ -7,40 +7,45 @@
-->
<div class="cv-tab-content">
<div class="cv-action-bar">
<mat-form-field appearance="outline" class="cv-search-field">
<mat-icon matPrefix>search</mat-icon>
<input
matInput
placeholder="{{ 'labels.text.Search by user' | translate }}"
(keyup)="applyFilter($event.target.value)"
/>
</mat-form-field>
<button
mat-icon-button
(click)="toggleAdvancedSearch()"
class="cv-toggle-btn"
[attr.aria-label]="
showAdvancedSearch
? ('labels.buttons.Hide advanced search' | translate)
: ('labels.buttons.Show advanced search' | translate)
"
[attr.aria-expanded]="showAdvancedSearch"
>
<mat-icon>{{ showAdvancedSearch ? 'arrow_drop_up' : 'arrow_drop_down' }}</mat-icon>
</button>
<div class="cv-action-buttons">
<button mat-stroked-button class="cv-btn cv-btn-approve" (click)="approveChecker()">
<fa-icon icon="check" class="m-r-10"></fa-icon>{{ 'labels.buttons.Approve' | translate }}
</button>
<button mat-stroked-button class="cv-btn cv-btn-delete" (click)="deleteChecker()">
<fa-icon icon="trash" class="m-r-10"></fa-icon>{{ 'labels.buttons.Delete' | translate }}
</button>
<button mat-stroked-button class="cv-btn cv-btn-reject" (click)="rejectChecker()">
<fa-icon icon="times" class="m-r-10"></fa-icon>{{ 'labels.buttons.Reject' | translate }}
<!-- Action bar (search + advanced toggle + Approve/Delete/Reject) — only
rendered when there's actual data to work with. An empty inbox shows
the friendly empty-state block below without these controls. -->
@if (checkerData) {
<div class="cv-action-bar">
<mat-form-field appearance="outline" class="cv-search-field">
<mat-icon matPrefix>search</mat-icon>
<input
matInput
placeholder="{{ 'labels.text.Search by user' | translate }}"
(keyup)="applyFilter($event.target.value)"
/>
</mat-form-field>
<button
mat-icon-button
(click)="toggleAdvancedSearch()"
class="cv-toggle-btn"
[attr.aria-label]="
showAdvancedSearch
? ('labels.buttons.Hide advanced search' | translate)
: ('labels.buttons.Show advanced search' | translate)
"
[attr.aria-expanded]="showAdvancedSearch"
>
<mat-icon>{{ showAdvancedSearch ? 'arrow_drop_up' : 'arrow_drop_down' }}</mat-icon>
</button>
<div class="cv-action-buttons">
<button mat-stroked-button class="cv-btn cv-btn-approve" (click)="approveChecker()">
<fa-icon icon="check" class="m-r-10"></fa-icon>{{ 'labels.buttons.Approve' | translate }}
</button>
<button mat-stroked-button class="cv-btn cv-btn-delete" (click)="deleteChecker()">
<fa-icon icon="trash" class="m-r-10"></fa-icon>{{ 'labels.buttons.Delete' | translate }}
</button>
<button mat-stroked-button class="cv-btn cv-btn-reject" (click)="rejectChecker()">
<fa-icon icon="times" class="m-r-10"></fa-icon>{{ 'labels.buttons.Reject' | translate }}
</button>
</div>
</div>
</div>
}
@if (showAdvancedSearch) {
<form [formGroup]="makerCheckerSearchForm">
@@ -161,16 +166,20 @@
}
@if (checkerData && noSearchedData) {
<div class="empty-state">
<div class="empty-state empty-state--filter">
<i class="fas fa-inbox empty-icon"></i>
<p class="empty-text">{{ 'labels.text.No checker inbox data available for this search' | translate }}</p>
<p class="empty-title">No matches</p>
<p class="empty-text">No checker inbox items match the current search. Try clearing the filters.</p>
</div>
}
@if (!checkerData) {
<div class="empty-state">
<i class="fas fa-inbox empty-icon"></i>
<p class="empty-text">{{ 'labels.text.No checker inbox data available for this account' | translate }}</p>
<p class="empty-title">Inbox is empty</p>
<p class="empty-text">
Currently no items in the Checker Inbox. New maker actions awaiting review will appear here.
</p>
</div>
}
</div>