CIBIL, Experian, Equifax and CRIF are vendors under API Management (active = whitelisted) and the Credit Bureau dashboard reads them from there; the separate CIC whitelist and API mapping screens are removed. Mapping tables show the value each row finds in the sample payload, and bureau cards link to their set-up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
131 lines
3.9 KiB
TypeScript
131 lines
3.9 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 { CommonModule } from '@angular/common';
|
|
import { Component, OnInit, inject } from '@angular/core';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { RouterModule } from '@angular/router';
|
|
|
|
import { ApiManagementService } from 'app/api-management/api-management.service';
|
|
import { HasPermissionDirective } from 'app/directives/has-permission/has-permission.directive';
|
|
import { CicAlertDetailComponent } from '../cic-alert-detail/cic-alert-detail.component';
|
|
import { CicService, cicError, cicStatusTone } from '../cic.service';
|
|
|
|
/**
|
|
* Credit Bureau dashboard: our customers who applied for a loan at another bank / NBFC, as reported by the CICs -
|
|
* who, what loan and how much (the CIC does not name the lender) - with the customers applying elsewhere repeatedly,
|
|
* the loan types, the trend, and the fetch status of each bureau. Each bureau is set up under API Management → API
|
|
* Vendors & Setup.
|
|
*/
|
|
@Component({
|
|
selector: 'mifosx-cic-dashboard',
|
|
standalone: true,
|
|
imports: [
|
|
CommonModule,
|
|
FormsModule,
|
|
RouterModule,
|
|
HasPermissionDirective,
|
|
CicAlertDetailComponent
|
|
],
|
|
templateUrl: './cic-dashboard.component.html',
|
|
styleUrls: ['../cic-shared.scss']
|
|
})
|
|
export class CicDashboardComponent implements OnInit {
|
|
private cic = inject(CicService);
|
|
private apis = inject(ApiManagementService);
|
|
|
|
/** Each bureau is set up as a vendor under API Management (connection, whitelist, request / response mapping). */
|
|
setupRoute(code: string): any[] {
|
|
const v = this.apis.vendorByCode(code);
|
|
return v ? [
|
|
'/api-management/vendors',
|
|
v.id
|
|
] : ['/api-management/vendors'];
|
|
}
|
|
|
|
days = 30;
|
|
loading = false;
|
|
error: string | null = null;
|
|
info: string | null = null;
|
|
data: any = null;
|
|
fetching: number | 'ALL' | null = null;
|
|
openAlertId: number | null = null;
|
|
|
|
readonly statusTone = cicStatusTone;
|
|
|
|
ngOnInit(): void {
|
|
this.load();
|
|
}
|
|
|
|
load(): void {
|
|
this.loading = true;
|
|
this.error = null;
|
|
this.cic.dashboard(this.days).subscribe({
|
|
next: (d) => {
|
|
this.data = d;
|
|
this.loading = false;
|
|
},
|
|
error: (e) => {
|
|
this.error = cicError(e, 'Could not load the credit bureau dashboard.');
|
|
this.loading = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
get whitelisted(): any[] {
|
|
return (this.data?.bureaus ?? []).filter((b: any) => b.whitelisted);
|
|
}
|
|
|
|
/** Fetch now from one bureau, or from every whitelisted bureau in turn. */
|
|
fetchNow(bureauId: number | 'ALL'): void {
|
|
const ids = bureauId === 'ALL' ? this.whitelisted.map((b) => b.id as number) : [bureauId];
|
|
if (!ids.length) return;
|
|
this.fetching = bureauId;
|
|
this.error = null;
|
|
this.info = null;
|
|
const messages: string[] = [];
|
|
const next = (i: number) => {
|
|
if (i >= ids.length) {
|
|
this.fetching = null;
|
|
this.info = messages.join(' · ');
|
|
this.load();
|
|
return;
|
|
}
|
|
this.cic.fetch(ids[i]).subscribe({
|
|
next: (run) => {
|
|
messages.push(
|
|
`${run.bureauCode}: ${run.status === 'SUCCESS' ? run.newAlerts + ' new alert(s)' : 'failed - ' + run.message}`
|
|
);
|
|
next(i + 1);
|
|
},
|
|
error: (e) => {
|
|
messages.push(cicError(e, 'Fetch failed.'));
|
|
next(i + 1);
|
|
}
|
|
});
|
|
};
|
|
next(0);
|
|
}
|
|
|
|
max(list: any[] | undefined, key: string): number {
|
|
return Math.max(1, ...(list ?? []).map((x) => Number(x[key] ?? 0)));
|
|
}
|
|
|
|
pct(value: any, list: any[] | undefined, key: string): number {
|
|
return Math.round((Number(value ?? 0) / this.max(list, key)) * 100);
|
|
}
|
|
|
|
onChanged(): void {
|
|
this.load();
|
|
}
|
|
|
|
fetchTone(status?: string): string {
|
|
return status === 'SUCCESS' ? 'good' : status === 'FAILED' ? 'bad' : '';
|
|
}
|
|
}
|