Files
cbsnexora/src/app/clients/clients-view/ckyc-tab/ckyc-tab.component.ts
T
AdityaandClaude Opus 4.6 73aa69ee02 feat: add RBI circulars, CKYC, KYC, Superset analytics, passbook modules and UI updates
Add new feature modules (RBI feeds, CKYC, KYC tab, Form60, Superset analytics, passbook),
update client family members, dashboard, login page, sidenav, images, translations,
environment config, and fix RBI feeds API auth by using relative URLs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:13:35 +05:30

220 lines
6.0 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/.
*/
/** Angular Imports */
import { Component, OnInit, OnDestroy, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subject, takeUntil, catchError, of } from 'rxjs';
/** Custom Services */
import { ClientsService } from '../../clients.service';
import { AlertService } from 'app/core/alert/alert.service';
import { MatIcon } from '@angular/material/icon';
import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@Component({
selector: 'mifosx-ckyc-tab',
templateUrl: './ckyc-tab.component.html',
styleUrls: ['./ckyc-tab.component.scss'],
standalone: true,
imports: [
...STANDALONE_SHARED_IMPORTS,
MatIcon,
MatProgressSpinner,
FaIconComponent
]
})
export class CkycTabComponent implements OnInit, OnDestroy {
private route = inject(ActivatedRoute);
private clientsService = inject(ClientsService);
private alertService = inject(AlertService);
private destroy$ = new Subject<void>();
/** Top section - read-only client info */
clientId: string;
clientName: string = '';
allAccounts: any[] = [];
selectedAccount: any = null;
selectedAccountType: string = '';
/** State */
loading = true;
ckycData: any = null;
/** File references */
addressProofFile: File | null = null;
photoProofFile: File | null = null;
clientImageFile: File | null = null;
clientSignatureFile: File | null = null;
/** Per-document upload state */
uploading: { [key: string]: boolean } = {
ADDRESS_PROOF: false,
PHOTO_PROOF: false,
CLIENT_IMAGE: false,
CLIENT_SIGNATURE: false
};
/** Per-document upload response */
uploadResponses: { [key: string]: any } = {};
/** Per-document error messages */
uploadErrors: { [key: string]: string | null } = {
ADDRESS_PROOF: null,
PHOTO_PROOF: null,
CLIENT_IMAGE: null,
CLIENT_SIGNATURE: null
};
ngOnInit(): void {
this.route.parent.params.subscribe((params: any) => {
this.clientId = params['clientId'];
this.loadClientInfo();
this.loadAccounts();
this.loadCkycData();
});
}
private loadClientInfo(): void {
this.route.parent.data.subscribe((data: any) => {
if (data?.clientViewData) {
this.clientName = data.clientViewData.displayName || '';
}
});
}
private loadAccounts(): void {
this.clientsService
.getClientAccountData(this.clientId)
.pipe(
takeUntil(this.destroy$),
catchError(() => of(null))
)
.subscribe((data: any) => {
if (data) {
const savings = (data.savingsAccounts || []).map((a: any) => ({
accountNo: a.accountNo,
accountType: 'Savings',
productName: a.productName || '',
id: a.id
}));
const loans = (data.loanAccounts || []).map((a: any) => ({
accountNo: a.accountNo,
accountType: 'Loan',
productName: a.productName || '',
id: a.id
}));
this.allAccounts = [
...savings,
...loans
];
}
});
}
private loadCkycData(): void {
this.loading = true;
this.clientsService
.getClientCkyc(this.clientId)
.pipe(
takeUntil(this.destroy$),
catchError(() => {
this.loading = false;
return of(null);
})
)
.subscribe((data: any) => {
this.loading = false;
this.ckycData = data;
});
}
onAccountSelect(accountNo: string): void {
this.selectedAccount = this.allAccounts.find((a) => a.accountNo === accountNo) || null;
this.selectedAccountType = this.selectedAccount?.accountType || '';
}
onFileSelect(event: any, documentType: string): void {
const file = event.target?.files?.[0];
if (!file) return;
switch (documentType) {
case 'ADDRESS_PROOF':
this.addressProofFile = file;
break;
case 'PHOTO_PROOF':
this.photoProofFile = file;
break;
case 'CLIENT_IMAGE':
this.clientImageFile = file;
break;
case 'CLIENT_SIGNATURE':
this.clientSignatureFile = file;
break;
}
this.uploadErrors[documentType] = null;
}
getFile(documentType: string): File | null {
switch (documentType) {
case 'ADDRESS_PROOF':
return this.addressProofFile;
case 'PHOTO_PROOF':
return this.photoProofFile;
case 'CLIENT_IMAGE':
return this.clientImageFile;
case 'CLIENT_SIGNATURE':
return this.clientSignatureFile;
default:
return null;
}
}
uploadDocument(documentType: string): void {
const file = this.getFile(documentType);
if (!file) return;
this.uploading[documentType] = true;
this.uploadErrors[documentType] = null;
const formData = new FormData();
formData.append('file', file);
formData.append('documentType', documentType);
this.clientsService
.uploadCkycDocument(this.clientId, formData)
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (response: any) => {
this.uploading[documentType] = false;
this.uploadResponses[documentType] = response;
this.alertService.alert({
type: 'success',
message: response?.message || 'Document uploaded successfully.'
});
},
error: (err: any) => {
this.uploading[documentType] = false;
this.uploadErrors[documentType] =
err?.error?.developerMessage ||
err?.error?.defaultUserMessage ||
err?.message ||
'Failed to upload document.';
}
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}