/** * 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' : ''; } }