The "Add Member" dialog closed silently on submit, giving no acknowledgement of what got recorded — most notably the linked client's own CBS Client ID when the member was linked to an existing client rather than entered manually. Added a confirmation popup showing Client ID (when linked), name, relationship, DOB and dependent status right after submit.
609 lines
20 KiB
TypeScript
609 lines
20 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 { Component, Input, OnInit, inject } from '@angular/core';
|
|
import { MatDialog } from '@angular/material/dialog';
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
|
|
import { ClientFamilyMemberDialogComponent } from './client-family-member-dialog/client-family-member-dialog.component';
|
|
import { ImageZoomDialogComponent } from './client-family-member-dialog/client-family-member-dialog.component';
|
|
import { FamilyMemberAddedDialogComponent } from './family-member-added-dialog.component';
|
|
import { ClientsService } from '../../clients.service';
|
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
|
import { MatIcon } from '@angular/material/icon';
|
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
|
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
|
import { FindPipe } from '../../../pipes/find.pipe';
|
|
import { DateFormatPipe } from '../../../pipes/date-format.pipe';
|
|
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
import { MatTooltip } from '@angular/material/tooltip';
|
|
|
|
@Component({
|
|
selector: 'mifosx-client-family-members-step',
|
|
templateUrl: './client-family-members-step.component.html',
|
|
styleUrls: ['./client-family-members-step.component.scss'],
|
|
imports: [
|
|
...STANDALONE_SHARED_IMPORTS,
|
|
FormsModule,
|
|
FaIconComponent,
|
|
MatIcon,
|
|
MatSlideToggle,
|
|
MatProgressSpinner,
|
|
FindPipe,
|
|
DateFormatPipe,
|
|
MatTooltip
|
|
]
|
|
})
|
|
export class ClientFamilyMembersStepComponent implements OnInit {
|
|
dialog = inject(MatDialog);
|
|
private translateService = inject(TranslateService);
|
|
private clientsService = inject(ClientsService);
|
|
private sanitizer = inject(DomSanitizer);
|
|
|
|
@Input() clientTemplate: any;
|
|
@Input() clientId: string = '';
|
|
@Input() maritalStatusId: number | null = null;
|
|
@Input() maritalStatusOptions: any[] = [];
|
|
/** Read-only mode — hides add/edit/delete buttons */
|
|
@Input() readOnly = false;
|
|
/**
|
|
* When true, hide the family-member section entirely. Used for clients that
|
|
* are eligible for nominees but not for family members (e.g. proprietorship
|
|
* entities — see {@link nomineeAllowed} / {@link familyAllowed}).
|
|
*/
|
|
@Input() nomineeOnly = false;
|
|
|
|
/** Family Members */
|
|
clientFamilyMembers: any[] = [];
|
|
private loaded = false;
|
|
|
|
/** Nominees */
|
|
nominees: any[] = [];
|
|
private nomineesLoaded = false;
|
|
showNomineeForm = false;
|
|
|
|
/** Nominee search */
|
|
nomineeSearchText = '';
|
|
nomineeSearchResults: any[] = [];
|
|
nomineeSearching = false;
|
|
private nomineeSearchTimer: any = null;
|
|
|
|
/** Selected nominee client */
|
|
selectedNomineeClient: any = null;
|
|
nomineeRelationshipId: number | null = null;
|
|
nomineePercentageShare: number | null = null;
|
|
nomineeIsMinor = false;
|
|
nomineeSaving = false;
|
|
nomineeType = 'DA1';
|
|
|
|
/** Nomination type options */
|
|
nominationTypeOptions = [
|
|
{ value: 'DA1', label: 'DA1 - Add Nominee' },
|
|
{ value: 'DA5', label: 'DA5 - Modify / Cancel Nominee' }
|
|
];
|
|
|
|
/** Guardian search (for minor nominee) */
|
|
guardianSearchText = '';
|
|
guardianSearchResults: any[] = [];
|
|
guardianSearching = false;
|
|
selectedGuardian: any = null;
|
|
guardianRelationship = '';
|
|
private guardianSearchTimer: any = null;
|
|
|
|
/** Relationship options */
|
|
relationshipOptions = [
|
|
'Father',
|
|
'Mother',
|
|
'Spouse',
|
|
'Son',
|
|
'Daughter',
|
|
'Brother',
|
|
'Sister',
|
|
'Uncle',
|
|
'Aunt',
|
|
'Grandfather',
|
|
'Grandmother',
|
|
'Friend',
|
|
'Business Partner',
|
|
'Other'
|
|
];
|
|
|
|
ngOnInit(): void {
|
|
if (this.clientId) {
|
|
this.loadExistingMembers();
|
|
this.loadNominees();
|
|
}
|
|
}
|
|
|
|
// ═══ Family Members ═══
|
|
|
|
private loadExistingMembers(): void {
|
|
if (this.loaded) return;
|
|
this.clientsService.getClientFamilyMembers(this.clientId).subscribe({
|
|
next: (data: any) => {
|
|
this.loaded = true;
|
|
this.clientFamilyMembers = Array.isArray(data) ? data : [];
|
|
},
|
|
error: () => {
|
|
this.loaded = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
addFamilyMember() {
|
|
if (this.nomineeOnly || this.readOnly) return;
|
|
const ref = this.dialog.open(ClientFamilyMemberDialogComponent, {
|
|
data: {
|
|
context: 'Add',
|
|
options: this.clientTemplate.familyMemberOptions,
|
|
existingMembers: this.clientFamilyMembers,
|
|
hostClientId: this.clientId
|
|
},
|
|
width: '640px',
|
|
maxWidth: '95vw',
|
|
panelClass: 'fm-dialog-panel'
|
|
});
|
|
ref.afterClosed().subscribe((response: any) => {
|
|
if (response?.member) {
|
|
this.clientFamilyMembers.push(response.member);
|
|
this.showFamilyMemberAddedConfirmation(response.member);
|
|
}
|
|
});
|
|
}
|
|
|
|
/** Confirms what was just recorded — in particular the linked client's own CBS Client ID,
|
|
* since the "Add Member" dialog closes silently otherwise and gives no acknowledgement. */
|
|
private showFamilyMemberAddedConfirmation(member: any): void {
|
|
this.dialog.open(FamilyMemberAddedDialogComponent, {
|
|
width: '360px',
|
|
data: {
|
|
clientId: member._linkedClient?.id ?? member.linkedClientId ?? null,
|
|
name: member._linkedClient?.displayName || member.firstName,
|
|
relationship: this.getRelName(member.relationshipId),
|
|
dateOfBirth: member.dateOfBirth,
|
|
isDependent: member.isDependent
|
|
}
|
|
});
|
|
}
|
|
|
|
viewFamilyMember(member: any) {
|
|
this.dialog.open(ClientFamilyMemberDialogComponent, {
|
|
data: {
|
|
context: 'View',
|
|
member,
|
|
options: this.clientTemplate.familyMemberOptions,
|
|
existingMembers: this.clientFamilyMembers,
|
|
hostClientId: this.clientId
|
|
},
|
|
width: '640px',
|
|
maxWidth: '95vw',
|
|
panelClass: 'fm-dialog-panel'
|
|
});
|
|
}
|
|
|
|
editFamilyMember(member: any, index: number) {
|
|
const ref = this.dialog.open(ClientFamilyMemberDialogComponent, {
|
|
data: {
|
|
context: 'Edit',
|
|
member,
|
|
options: this.clientTemplate.familyMemberOptions,
|
|
existingMembers: this.clientFamilyMembers,
|
|
hostClientId: this.clientId
|
|
},
|
|
width: '640px',
|
|
maxWidth: '95vw',
|
|
panelClass: 'fm-dialog-panel'
|
|
});
|
|
ref.afterClosed().subscribe((response: any) => {
|
|
if (!response?.member) return;
|
|
const updated = response.member;
|
|
this.clientFamilyMembers.splice(index, 1, updated);
|
|
// Persist to backend if this is an existing (already-saved) member
|
|
if (member.id && this.clientId) {
|
|
const { id, _linkedClient, _marriageCertFile, ...payload } = updated;
|
|
this.clientsService.editFamilyMember(this.clientId, member.id, payload).subscribe({ error: () => {} });
|
|
}
|
|
});
|
|
}
|
|
|
|
deleteFamilyMember(name: string, index: number) {
|
|
const ref = this.dialog.open(DeleteDialogComponent, {
|
|
data: { deleteContext: `${name}` }
|
|
});
|
|
ref.afterClosed().subscribe((response: any) => {
|
|
if (!response?.delete) return;
|
|
const member = this.clientFamilyMembers[index];
|
|
// Persist deletion to backend for existing members; locally-added ones just drop from the array
|
|
if (member?.id && this.clientId) {
|
|
this.clientsService.deleteFamilyMember(this.clientId, member.id.toString()).subscribe({
|
|
next: () => this.clientFamilyMembers.splice(index, 1),
|
|
error: () => this.clientFamilyMembers.splice(index, 1)
|
|
});
|
|
} else {
|
|
this.clientFamilyMembers.splice(index, 1);
|
|
}
|
|
});
|
|
}
|
|
|
|
get familyMembers() {
|
|
return { familyMembers: this.clientFamilyMembers };
|
|
}
|
|
|
|
get hasFamilyMembers(): boolean {
|
|
return this.clientFamilyMembers.length > 0;
|
|
}
|
|
|
|
/** True if a family member with the given relationship (case-insensitive) has been added */
|
|
private hasRelationship(name: string): boolean {
|
|
const target = name.toLowerCase();
|
|
return this.clientFamilyMembers.some((m: any) => {
|
|
const resolved = this.getRelName(m.relationshipId ?? m.relationship).toLowerCase();
|
|
if (resolved === target) return true;
|
|
return typeof m.relationship === 'string' && m.relationship.toLowerCase() === target;
|
|
});
|
|
}
|
|
|
|
get hasMother(): boolean {
|
|
return this.hasRelationship('mother');
|
|
}
|
|
|
|
get hasFather(): boolean {
|
|
return this.hasRelationship('father');
|
|
}
|
|
|
|
get hasCaretaker(): boolean {
|
|
return this.hasRelationship('caretaker');
|
|
}
|
|
|
|
/** Mother is always required; Father is required unless a Caretaker has been added instead. */
|
|
get familyRelationshipRulesMet(): boolean {
|
|
return this.hasMother && (this.hasFather || this.hasCaretaker);
|
|
}
|
|
|
|
/** Check if client's marital status is "Married" */
|
|
get isMarried(): boolean {
|
|
if (!this.maritalStatusId || !this.maritalStatusOptions?.length) return false;
|
|
const selected = this.maritalStatusOptions.find((o: any) => o.id === this.maritalStatusId);
|
|
return selected?.name?.toLowerCase().includes('married') || false;
|
|
}
|
|
|
|
/** Check if a spouse has been added */
|
|
get hasSpouse(): boolean {
|
|
return this.clientFamilyMembers.some((m: any) => {
|
|
const rel = m.relationship || m.relationshipId;
|
|
const relName = typeof rel === 'string' ? rel : '';
|
|
return relName.toLowerCase() === 'spouse' || m.firstName?.toLowerCase().includes('spouse');
|
|
});
|
|
}
|
|
|
|
// ═══ Nominees ═══
|
|
|
|
private loadNominees(): void {
|
|
if (this.nomineesLoaded || !this.clientId) return;
|
|
// Nominees are stored as family members with nominationType set
|
|
this.clientsService.getClientFamilyMembers(this.clientId).subscribe({
|
|
next: (data: any) => {
|
|
this.nomineesLoaded = true;
|
|
const allMembers = Array.isArray(data) ? data : [];
|
|
this.nominees = allMembers.filter((m: any) => m.nominationType && m.nominationType !== '');
|
|
},
|
|
error: () => {
|
|
this.nomineesLoaded = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
onNomineeSearch(text: string): void {
|
|
this.nomineeSearchText = text;
|
|
clearTimeout(this.nomineeSearchTimer);
|
|
if (!text || text.length < 2) {
|
|
this.nomineeSearchResults = [];
|
|
return;
|
|
}
|
|
this.nomineeSearching = true;
|
|
this.nomineeSearchTimer = setTimeout(() => {
|
|
// Nominee picker: a nominee must be a natural person, never a corporate entity.
|
|
this.clientsService.searchByText(text, 0, 10, '', '', true, 'INDIVIDUAL').subscribe({
|
|
next: (data: any) => {
|
|
this.nomineeSearchResults = data?.content || [];
|
|
this.nomineeSearching = false;
|
|
this.clientsService.enrichSearchResults(this.nomineeSearchResults);
|
|
},
|
|
error: () => {
|
|
this.nomineeSearchResults = [];
|
|
this.nomineeSearching = false;
|
|
}
|
|
});
|
|
}, 400);
|
|
}
|
|
|
|
selectNomineeClient(client: any): void {
|
|
this.selectedNomineeClient = { ...client, _loading: true };
|
|
this.nomineeSearchResults = [];
|
|
this.nomineeSearchText = '';
|
|
this.fetchNomineeClientDetails(client.id);
|
|
}
|
|
|
|
private fetchNomineeClientDetails(clientId: number): void {
|
|
const details: any = {};
|
|
let pending = 4;
|
|
const done = () => {
|
|
pending--;
|
|
if (pending <= 0) {
|
|
this.selectedNomineeClient = { ...this.selectedNomineeClient, ...details, _loading: false };
|
|
}
|
|
};
|
|
|
|
// Fetch client data for DOB
|
|
this.clientsService.getClientData(clientId.toString()).subscribe({
|
|
next: (data: any) => {
|
|
if (data?.dateOfBirth) {
|
|
details.dateOfBirth = data.dateOfBirth;
|
|
}
|
|
done();
|
|
},
|
|
error: () => done()
|
|
});
|
|
|
|
// Fetch KYC for PAN/Aadhaar + verification status
|
|
this.clientsService.getClientKyc(clientId.toString()).subscribe({
|
|
next: (kyc: any) => {
|
|
details.panNumber = kyc?.panNumber || null;
|
|
details.aadhaarMasked = kyc?.aadhaarNumberMasked || null;
|
|
details.panVerified = kyc?.panVerified || false;
|
|
details.aadhaarVerified = kyc?.aadhaarVerified || false;
|
|
done();
|
|
},
|
|
error: () => done()
|
|
});
|
|
|
|
// Fetch profile image
|
|
this.clientsService.getClientProfileImage(clientId.toString()).subscribe({
|
|
next: (img: any) => {
|
|
if (img) {
|
|
details.profileImageUrl = this.sanitizer.bypassSecurityTrustResourceUrl(img);
|
|
}
|
|
done();
|
|
},
|
|
error: () => done()
|
|
});
|
|
|
|
// Fetch signature image
|
|
this.clientsService.getClientDocuments(clientId.toString()).subscribe({
|
|
next: (docs: any[]) => {
|
|
const sigDoc = docs?.find(
|
|
(d: any) => d.name?.toLowerCase().includes('signature') || d.description?.toLowerCase().includes('signature')
|
|
);
|
|
if (sigDoc) {
|
|
this.clientsService.getClientSignatureImage(clientId.toString(), sigDoc.id.toString()).subscribe({
|
|
next: (blob: Blob) => {
|
|
details.signatureUrl = this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(blob));
|
|
done();
|
|
},
|
|
error: () => done()
|
|
});
|
|
} else {
|
|
done();
|
|
}
|
|
},
|
|
error: () => done()
|
|
});
|
|
}
|
|
|
|
onGuardianSearch(text: string): void {
|
|
this.guardianSearchText = text;
|
|
clearTimeout(this.guardianSearchTimer);
|
|
if (!text || text.length < 2) {
|
|
this.guardianSearchResults = [];
|
|
return;
|
|
}
|
|
this.guardianSearching = true;
|
|
this.guardianSearchTimer = setTimeout(() => {
|
|
// Guardian picker: a guardian is by definition a natural person responsible for a minor /
|
|
// dependent — non-individuals can never play that role.
|
|
this.clientsService.searchByText(text, 0, 10, '', '', true, 'INDIVIDUAL').subscribe({
|
|
next: (data: any) => {
|
|
this.guardianSearchResults = data?.content || [];
|
|
this.guardianSearching = false;
|
|
this.clientsService.enrichSearchResults(this.guardianSearchResults);
|
|
},
|
|
error: () => {
|
|
this.guardianSearchResults = [];
|
|
this.guardianSearching = false;
|
|
}
|
|
});
|
|
}, 400);
|
|
}
|
|
|
|
selectGuardian(client: any): void {
|
|
this.selectedGuardian = client;
|
|
this.guardianSearchResults = [];
|
|
this.guardianSearchText = '';
|
|
}
|
|
|
|
get existingShareTotal(): number {
|
|
return this.nominees.reduce((sum, n) => sum + (n.percentageShare || 0), 0);
|
|
}
|
|
|
|
get totalSharePercentage(): number {
|
|
return this.existingShareTotal + (this.nomineePercentageShare || 0);
|
|
}
|
|
|
|
get maxAllowedShare(): number {
|
|
return 100 - this.existingShareTotal;
|
|
}
|
|
|
|
saveNominee(): void {
|
|
if (!this.clientId || !this.selectedNomineeClient || !this.nomineeRelationshipId || !this.nomineePercentageShare)
|
|
return;
|
|
if (this.totalSharePercentage > 100) return;
|
|
|
|
this.nomineeSaving = true;
|
|
|
|
// Get relationship name for display
|
|
const relOption = this.clientTemplate?.familyMemberOptions?.relationshipIdOptions?.find(
|
|
(r: any) => r.id === this.nomineeRelationshipId
|
|
);
|
|
const relName = relOption?.name || '';
|
|
|
|
const payload: any = {
|
|
firstName: this.selectedNomineeClient.displayName,
|
|
relationshipId: this.nomineeRelationshipId,
|
|
nominationType: this.nomineeType,
|
|
percentageShare: this.nomineePercentageShare,
|
|
isDependent: this.nomineeIsMinor,
|
|
dateFormat: 'dd MMMM yyyy',
|
|
locale: 'en'
|
|
};
|
|
|
|
if (this.nomineeIsMinor && this.selectedGuardian) {
|
|
payload.guardianName = this.selectedGuardian.displayName;
|
|
payload.guardianRelationship = this.guardianRelationship;
|
|
}
|
|
|
|
this.clientsService.addFamilyMember(this.clientId, payload).subscribe({
|
|
next: (res: any) => {
|
|
this.nomineeSaving = false;
|
|
this.nominees.push({
|
|
id: res?.resourceId,
|
|
...payload,
|
|
relationship: relName,
|
|
nomineeName: this.selectedNomineeClient.displayName,
|
|
nomineeCif: this.selectedNomineeClient.accountNo,
|
|
nomineeMobile: this.selectedNomineeClient.mobileNo,
|
|
nomineeDob: this.selectedNomineeClient.dateOfBirth,
|
|
profileImageUrl: this.selectedNomineeClient.profileImageUrl,
|
|
signatureUrl: this.selectedNomineeClient.signatureUrl,
|
|
panNumber: this.selectedNomineeClient.panNumber,
|
|
aadhaarMasked: this.selectedNomineeClient.aadhaarMasked,
|
|
guardianName: this.selectedGuardian?.displayName
|
|
});
|
|
this.resetNomineeForm();
|
|
},
|
|
error: () => {
|
|
this.nomineeSaving = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
/** Editing nominee state */
|
|
editingNomineeIndex: number | null = null;
|
|
editNomineeRelId: number | null = null;
|
|
editNomineeShare: number | null = null;
|
|
editNomineeType: string = 'DA1';
|
|
|
|
editNominee(nominee: any, index: number): void {
|
|
this.editingNomineeIndex = index;
|
|
this.editNomineeRelId = nominee.relationshipId?.id || nominee.relationshipId || null;
|
|
this.editNomineeShare = nominee.percentageShare;
|
|
this.editNomineeType = nominee.nominationType || 'DA1';
|
|
}
|
|
|
|
cancelEditNominee(): void {
|
|
this.editingNomineeIndex = null;
|
|
}
|
|
|
|
/** Max share allowed for the nominee being edited (excludes its own share) */
|
|
maxShareForEdit(index: number): number {
|
|
const otherTotal = this.nominees.reduce((sum, n, i) => (i === index ? sum : sum + (n.percentageShare || 0)), 0);
|
|
return 100 - otherTotal;
|
|
}
|
|
|
|
saveEditNominee(nominee: any, index: number): void {
|
|
if (!this.editNomineeRelId || !this.editNomineeShare) return;
|
|
const otherTotal = this.nominees.reduce((sum, n, i) => (i === index ? sum : sum + (n.percentageShare || 0)), 0);
|
|
if (otherTotal + this.editNomineeShare > 100) return;
|
|
|
|
const relOption = this.clientTemplate?.familyMemberOptions?.relationshipIdOptions?.find(
|
|
(r: any) => r.id === this.editNomineeRelId
|
|
);
|
|
|
|
// Update in-memory
|
|
nominee.relationshipId = this.editNomineeRelId;
|
|
nominee.relationship = relOption?.name || nominee.relationship;
|
|
nominee.percentageShare = this.editNomineeShare;
|
|
nominee.nominationType = this.editNomineeType;
|
|
|
|
// If saved to DB, update via API
|
|
if (nominee.id && this.clientId) {
|
|
const payload: any = {
|
|
firstName: nominee.firstName || nominee.nomineeName,
|
|
relationshipId: this.editNomineeRelId,
|
|
nominationType: this.editNomineeType,
|
|
percentageShare: this.editNomineeShare,
|
|
isDependent: nominee.isDependent || false,
|
|
dateFormat: 'dd MMMM yyyy',
|
|
locale: 'en'
|
|
};
|
|
if (nominee.guardianName) {
|
|
payload.guardianName = nominee.guardianName;
|
|
}
|
|
if (nominee.guardianRelationship) {
|
|
payload.guardianRelationship = nominee.guardianRelationship;
|
|
}
|
|
this.clientsService.editFamilyMember(this.clientId, nominee.id, payload).subscribe({ error: () => {} });
|
|
}
|
|
|
|
this.editingNomineeIndex = null;
|
|
}
|
|
|
|
deleteNominee(nominee: any, index: number): void {
|
|
const ref = this.dialog.open(DeleteDialogComponent, {
|
|
data: { deleteContext: `nominee ${nominee.nomineeName || nominee.firstName || ''}` }
|
|
});
|
|
ref.afterClosed().subscribe((response: any) => {
|
|
if (response?.delete) {
|
|
if (nominee.id && this.clientId) {
|
|
this.clientsService.deleteFamilyMember(this.clientId, nominee.id.toString()).subscribe({
|
|
next: () => this.nominees.splice(index, 1),
|
|
error: () => this.nominees.splice(index, 1)
|
|
});
|
|
} else {
|
|
this.nominees.splice(index, 1);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
resetNomineeForm(): void {
|
|
this.showNomineeForm = false;
|
|
this.selectedNomineeClient = null;
|
|
this.nomineeRelationshipId = null;
|
|
this.nomineePercentageShare = null;
|
|
this.nomineeIsMinor = false;
|
|
this.nomineeType = 'DA1';
|
|
this.selectedGuardian = null;
|
|
this.guardianRelationship = '';
|
|
this.nomineeSearchText = '';
|
|
this.guardianSearchText = '';
|
|
}
|
|
|
|
/** Open image in zoom overlay */
|
|
zoomImage(imageSrc: any, title: string): void {
|
|
this.dialog.open(ImageZoomDialogComponent, {
|
|
data: { imageSrc, title },
|
|
panelClass: 'image-zoom-panel',
|
|
maxWidth: '90vw',
|
|
maxHeight: '90vh'
|
|
});
|
|
}
|
|
|
|
/** Get relationship name from ID */
|
|
getRelName(relId: any): string {
|
|
if (typeof relId === 'object' && relId?.name) return relId.name;
|
|
const opt = this.clientTemplate?.familyMemberOptions?.relationshipIdOptions?.find((r: any) => r.id === relId);
|
|
return opt?.name || '';
|
|
}
|
|
|
|
get nomineeData(): any[] {
|
|
return this.nominees;
|
|
}
|
|
}
|