Files
cbsnexora/src/app/clients/utils/family-nominee.guard.ts
T
AdityaandClaude Opus 4.7 b149fef72e 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>
2026-05-21 18:49:43 +05:30

67 lines
2.5 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/.
*/
import { inject } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivateFn, Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { ClientsService } from '../clients.service';
import { familyAllowed, nomineeAllowed } from './client-rules';
/**
* Walk up the route snapshot tree until we find a `clientId` path param. The
* family-members / nominees routes are nested two levels under the client
* shell, so simple `route.params['clientId']` returns undefined.
*/
function findClientId(route: ActivatedRouteSnapshot): string | null {
let cur: ActivatedRouteSnapshot | null = route;
while (cur) {
const id = cur.params?.['clientId'];
if (id) return id;
cur = cur.parent;
}
return null;
}
/**
* Build a guard that redirects to the client's General tab when the client
* isn't eligible for the section. Resolves the client lazily so we don't
* couple the guard to any specific parent resolver.
*/
function makeEligibilityGuard(
predicate: (legalFormId: number | null | undefined, constitutionLabel?: string | null) => boolean
): CanActivateFn {
return (route) => {
const router = inject(Router);
const clientsService = inject(ClientsService);
const clientId = findClientId(route);
// No clientId in the URL — let the framework's own missing-param handling kick in.
if (!clientId) return true;
return clientsService.getClientData(clientId).pipe(
map((client: any) => {
const legalFormId = client?.legalForm?.id;
const constitutionLabel = client?.clientNonPersonDetails?.constitution?.name || '';
if (predicate(legalFormId, constitutionLabel)) return true;
return router.createUrlTree([
'/clients',
clientId,
'general'
]);
}),
// If the client lookup fails, fail open and let downstream handle it
// rather than redirecting users into an infinite loop on a stale URL.
catchError(() => of(true))
) as Observable<boolean | ReturnType<Router['createUrlTree']>>;
};
}
export const familyAllowedGuard: CanActivateFn = makeEligibilityGuard(familyAllowed);
export const nomineeAllowedGuard: CanActivateFn = makeEligibilityGuard(nomineeAllowed);