264 lines
7.1 KiB
TypeScript
264 lines
7.1 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, 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<NomineeData | null>();
|
|
|
|
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: () => {}
|
|
});
|
|
}
|
|
}
|