/** * 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, Output, EventEmitter, inject, OnInit } from '@angular/core'; import { UntypedFormBuilder, UntypedFormGroup, Validators, FormsModule } from '@angular/forms'; import { DomSanitizer } from '@angular/platform-browser'; import { ClientsService } from 'app/clients/clients.service'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { MatProgressSpinner } from '@angular/material/progress-spinner'; import { MatIconButton } from '@angular/material/button'; import { MatIcon } from '@angular/material/icon'; import { MatCheckbox } from '@angular/material/checkbox'; import { MatDatepickerModule } from '@angular/material/datepicker'; export interface NomineeData { mode: 'manual' | 'linked'; // Manual fields fullName?: string; relationship?: string; dateOfBirth?: string; nationality?: string; aadhaarNumber?: string; panNumber?: string; mobileNo?: string; nomineeShare?: number; isAlive?: boolean; isDependent?: boolean; guardianName?: string; guardianRelationship?: string; remarks?: string; // Linked client fields clientId?: number; displayName?: string; cifNumber?: string; } const RELATIONSHIPS = [ 'Spouse', 'Son', 'Daughter', 'Father', 'Mother', 'Brother', 'Sister', 'Grandfather', 'Grandmother', 'Grandson', 'Granddaughter', 'Uncle', 'Aunt', 'Nephew', 'Niece', 'Friend', 'Other' ]; @Component({ selector: 'mifosx-nominee-form', templateUrl: './nominee-form.component.html', styleUrls: ['./nominee-form.component.scss'], imports: [ ...STANDALONE_SHARED_IMPORTS, FormsModule, MatProgressSpinner, MatIconButton, MatIcon, MatCheckbox, MatDatepickerModule ] }) export class NomineeFormComponent implements OnInit { private fb = inject(UntypedFormBuilder); private clientsService = inject(ClientsService); private sanitizer = inject(DomSanitizer); @Output() nomineeChange = new EventEmitter(); mode: 'manual' | 'linked' = 'manual'; relationships = RELATIONSHIPS; // Manual form manualForm!: UntypedFormGroup; minDate = new Date(1900, 0, 1); maxDate = new Date(); // Link mode searchText = ''; searching = false; searchResults: any[] = []; selectedClient: any = null; fetchingLinkedData = false; linkedProfileImage: any = null; linkedKycStatus: any = null; clientDob = ''; clientAadhaar = ''; clientPan = ''; clientCifNumber = ''; linkedRelationship = ''; linkedShare = 100; private searchTimer: any; ngOnInit(): void { this.manualForm = this.fb.group({ fullName: [ '', Validators.required ], relationship: [ '', Validators.required ], dateOfBirth: [''], nationality: ['Indian'], aadhaarNumber: [''], panNumber: [''], mobileNo: [''], nomineeShare: [100], isAlive: [true], isDependent: [false], guardianName: [''], guardianRelationship: [''], remarks: [''] }); this.manualForm.valueChanges.subscribe(() => { if (this.mode === 'manual' && this.manualForm.valid) { this.emitManual(); } }); } switchMode(m: 'manual' | 'linked'): void { this.mode = m; if (m === 'manual') { this.clearLinked(); } else { this.manualForm.reset({ nationality: 'Indian', isAlive: true, isDependent: false, nomineeShare: 100 }); this.nomineeChange.emit(null); } } // ─── Manual ────────────────────────────────────── private emitManual(): void { const v = this.manualForm.value; this.nomineeChange.emit({ mode: 'manual', ...v }); } // ─── Link existing client ───────────────────────── onSearch(text: string): void { this.searchText = text; clearTimeout(this.searchTimer); if (!text || text.length < 2) { this.searchResults = []; return; } this.searching = true; this.searchTimer = setTimeout(() => { this.clientsService.searchByText(text, 0, 8).subscribe({ next: (data: any) => { this.searchResults = data?.content || []; this.searching = false; }, error: () => { this.searchResults = []; this.searching = false; } }); }, 400); } selectClient(client: any): void { this.selectedClient = client; this.searchResults = []; this.searchText = ''; this.fetchLinkedClientData(client.id); this.nomineeChange.emit({ mode: 'linked', clientId: client.id, displayName: client.displayName, cifNumber: client.cifNumber || client.cifNo || '' }); } removeLinked(): void { this.clearLinked(); this.nomineeChange.emit(null); } onLinkedRelationshipChange(rel: string): void { if (this.selectedClient) { this.nomineeChange.emit({ mode: 'linked', clientId: this.selectedClient.id, displayName: this.selectedClient.displayName, cifNumber: this.selectedClient.accountNo || this.selectedClient.cifNumber || '', relationship: rel, nomineeShare: this.linkedShare }); } } private clearLinked(): void { this.selectedClient = null; this.linkedProfileImage = null; this.linkedKycStatus = null; this.clientDob = ''; this.clientAadhaar = ''; this.clientPan = ''; this.clientCifNumber = ''; this.searchText = ''; this.searchResults = []; this.linkedRelationship = ''; this.linkedShare = 100; } private fetchLinkedClientData(clientId: string): void { this.fetchingLinkedData = true; this.clientsService.getClientData(clientId).subscribe({ next: (data: any) => { if (data.dateOfBirth) { const d = Array.isArray(data.dateOfBirth) ? new Date(data.dateOfBirth[0], data.dateOfBirth[1] - 1, data.dateOfBirth[2]) : new Date(data.dateOfBirth); this.clientDob = d.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }); } this.fetchingLinkedData = false; }, error: () => { this.fetchingLinkedData = false; } }); this.clientsService.getClientKyc(clientId).subscribe({ next: (kyc: any) => { if (kyc) { this.clientAadhaar = kyc.aadhaarNumberMasked || ''; this.clientPan = kyc.panNumber || ''; this.linkedKycStatus = { panVerified: kyc.panVerified || false, aadhaarVerified: kyc.aadhaarVerified || false }; } }, error: () => {} }); this.clientsService.getClientProfileImage(clientId).subscribe({ next: (imageData: any) => { if (imageData) { this.linkedProfileImage = this.sanitizer.bypassSecurityTrustResourceUrl(imageData); } }, error: () => {} }); } }