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
@@ -138,33 +138,40 @@
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<!-- Tab: Family Members / Nominees -->
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<fa-icon icon="users" class="tab-icon"></fa-icon>
|
||||
FAMILY
|
||||
</ng-template>
|
||||
@let familyTabIdx = legalFormType === 2 ? 4 : 3;
|
||||
<div class="tab-content" [class.tab-content--readonly]="isTabReadOnly(familyTabIdx)">
|
||||
<div class="tab-mode-bar">
|
||||
@if (isTabReadOnly(familyTabIdx)) {
|
||||
<button mat-flat-button class="tab-mode-btn tab-mode-btn--edit" (click)="enableTabEdit(familyTabIdx)">
|
||||
<mat-icon>edit</mat-icon> Edit
|
||||
</button>
|
||||
} @else {
|
||||
<button mat-stroked-button class="tab-mode-btn tab-mode-btn--cancel" (click)="cancelTabEdit(familyTabIdx)">
|
||||
<mat-icon>close</mat-icon> Cancel
|
||||
</button>
|
||||
}
|
||||
<!-- Tab: Family / Nominees — hidden for entity clients that aren't proprietorships -->
|
||||
@if (showFamilyTab) {
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<fa-icon icon="users" class="tab-icon"></fa-icon>
|
||||
{{ familyTabNomineeOnly ? 'NOMINEES' : 'FAMILY' }}
|
||||
</ng-template>
|
||||
@let familyTabIdx = legalFormType === 2 ? 4 : 3;
|
||||
<div class="tab-content" [class.tab-content--readonly]="isTabReadOnly(familyTabIdx)">
|
||||
<div class="tab-mode-bar">
|
||||
@if (isTabReadOnly(familyTabIdx)) {
|
||||
<button mat-flat-button class="tab-mode-btn tab-mode-btn--edit" (click)="enableTabEdit(familyTabIdx)">
|
||||
<mat-icon>edit</mat-icon> Edit
|
||||
</button>
|
||||
} @else {
|
||||
<button
|
||||
mat-stroked-button
|
||||
class="tab-mode-btn tab-mode-btn--cancel"
|
||||
(click)="cancelTabEdit(familyTabIdx)"
|
||||
>
|
||||
<mat-icon>close</mat-icon> Cancel
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<mifosx-client-family-members-step
|
||||
#clientFamilyMembers
|
||||
[clientTemplate]="clientDataAndTemplate"
|
||||
[clientId]="clientId"
|
||||
[readOnly]="isTabReadOnly(familyTabIdx)"
|
||||
[nomineeOnly]="familyTabNomineeOnly"
|
||||
></mifosx-client-family-members-step>
|
||||
</div>
|
||||
<mifosx-client-family-members-step
|
||||
#clientFamilyMembers
|
||||
[clientTemplate]="clientDataAndTemplate"
|
||||
[clientId]="clientId"
|
||||
[readOnly]="isTabReadOnly(familyTabIdx)"
|
||||
></mifosx-client-family-members-step>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab>
|
||||
}
|
||||
|
||||
<!-- Tab: Application Summary & Submit -->
|
||||
<mat-tab>
|
||||
@@ -182,6 +189,7 @@
|
||||
[guardianRelation]="clientGeneralStep?.createClientForm?.get('guardianDetails.guardianRelation')?.value || ''"
|
||||
[entityClassificationData]="clientEntityKycStep?.classificationData"
|
||||
[familyMembers]="clientFamilyMembersStep?.clientFamilyMembers || []"
|
||||
[constitutionLabel]="constitutionLabel"
|
||||
[maritalStatusId]="clientGeneralStep?.createClientForm?.get('maritalStatusId')?.value"
|
||||
[maritalStatusOptions]="clientGeneralStep?.maritalStatusOptions || []"
|
||||
[educationLevelOptions]="clientGeneralStep?.educationLevelOptions || []"
|
||||
|
||||
@@ -17,6 +17,7 @@ import { map } from 'rxjs/operators';
|
||||
import { ClientsService } from '../clients.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component';
|
||||
import { familyAllowed, nomineeAllowed } from '../utils/client-rules';
|
||||
|
||||
/** Custom Components */
|
||||
import { ClientGeneralStepComponent } from '../client-stepper/client-general-step/client-general-step.component';
|
||||
@@ -121,9 +122,40 @@ export class EditClientComponent {
|
||||
return this.clientGeneralStep?.createClientForm;
|
||||
}
|
||||
|
||||
/** Total tab count (+1 for Nominees, +1 for Non-Individual Entity KYC) */
|
||||
/**
|
||||
* Constitution display name pulled from the live general step (preferred —
|
||||
* stays in sync if the user changes constitution mid-edit) with a fallback
|
||||
* to the resolver-provided snapshot. Drives the proprietorship-only nominee
|
||||
* rule below.
|
||||
*/
|
||||
get constitutionLabel(): string {
|
||||
return (
|
||||
this.clientGeneralStep?.selectedConstitution?.name ||
|
||||
this.clientDataAndTemplate?.clientNonPersonDetails?.constitution?.name ||
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
/** Whether the Family/Nominee tab should appear at all. */
|
||||
get showFamilyTab(): boolean {
|
||||
return nomineeAllowed(this.legalFormType, this.constitutionLabel);
|
||||
}
|
||||
|
||||
/** True when the tab should render in nominee-only mode (family rows hidden). */
|
||||
get familyTabNomineeOnly(): boolean {
|
||||
return !familyAllowed(this.legalFormType, this.constitutionLabel) && this.showFamilyTab;
|
||||
}
|
||||
|
||||
/** Total tab count varies by legal form + constitution.
|
||||
* Individual: General + KYC + Address + Family + Preview = 5
|
||||
* Entity (proprietorship): General + KYC + S&S + Address + Nominees + Preview = 6
|
||||
* Entity (non-proprietor): General + KYC + S&S + Address + Preview = 5
|
||||
*/
|
||||
get totalTabs(): number {
|
||||
return this.legalFormType === 2 ? 6 : 5;
|
||||
let count = 4; // General + KYC + Address + Preview
|
||||
if (this.legalFormType === 2) count++; // S&S only for entities
|
||||
if (this.showFamilyTab) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Get constitution type from general step for entity KYC */
|
||||
@@ -411,9 +443,10 @@ export class EditClientComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
// Family tab
|
||||
// Family tab — only present for individuals and proprietorship entities.
|
||||
// Skip when hidden so Preview at the same index isn't mistaken for Family.
|
||||
const familyTabIdx = this.legalFormType === 2 ? 4 : 3;
|
||||
if (tabIndex === familyTabIdx) {
|
||||
if (this.showFamilyTab && tabIndex === familyTabIdx) {
|
||||
this.savingClient = true;
|
||||
if (this.clientFamilyMembersStep?.hasFamilyMembers) {
|
||||
this.saveClientFamilyMembers(this.clientId);
|
||||
|
||||
Reference in New Issue
Block a user