/** * 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, OnInit, inject, ViewChild, ElementRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { MatTableModule } from '@angular/material/table'; import { MatButtonModule } from '@angular/material/button'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { Chart, registerables } from 'chart.js'; import { TranslateModule } from '@ngx-translate/core'; import { CfcfrmsService } from '../cfcfrms.service'; import { StatusBadgeComponent } from '../components/status-badge/status-badge.component'; import { Complaint, DashboardStats } from '../models/cfcfrms.models'; Chart.register(...registerables); @Component({ selector: 'mifosx-cfcfrms-dashboard', standalone: true, imports: [ CommonModule, RouterModule, MatTableModule, MatButtonModule, MatProgressSpinnerModule, FaIconComponent, TranslateModule, StatusBadgeComponent ], templateUrl: './cfcfrms-dashboard.component.html', styleUrls: ['./cfcfrms-dashboard.component.scss'] }) export class CfcfrmsDashboardComponent implements OnInit { private cfcfrmsService = inject(CfcfrmsService); stats: DashboardStats; recentComplaints: Complaint[] = []; isLoading = true; displayedColumns = [ 'ticketId', 'complainantName', 'fraudAmount', 'status', 'createdAt', 'actions' ]; @ViewChild('donutCanvas') donutCanvas: ElementRef; @ViewChild('barCanvas') barCanvas: ElementRef; private donutChart: any; private barChart: any; ngOnInit(): void { this.loadData(); } loadData(): void { this.isLoading = true; this.cfcfrmsService.getStats().subscribe({ next: (stats) => { this.stats = stats; setTimeout(() => this.renderCharts(), 100); }, error: () => { this.stats = { totalComplaints: 0, pendingReview: 0, lienMarked: 0, fundsRecovered: 0, statusDistribution: {}, amountByStatus: {} }; } }); this.cfcfrmsService.getComplaints({ page: 0, size: 5 }).subscribe({ next: (res) => { this.recentComplaints = res.content; this.isLoading = false; }, error: () => { this.recentComplaints = []; this.isLoading = false; } }); } private renderCharts(): void { if (!this.stats) return; const statusColors: Record = { RECEIVED: '#1E88E5', UNDER_REVIEW: '#F59E0B', LIEN_MARKED: '#EAB308', DEBIT_FROZEN: '#EF4444', FUNDS_MOVED: '#8B5CF6', RESOLVED: '#10B981', REJECTED: '#6B7280' }; // Donut chart if (this.donutCanvas) { const dist = this.stats.statusDistribution || {}; const labels = Object.keys(dist); const data = Object.values(dist); const colors = labels.map((l) => statusColors[l] || '#999'); if (this.donutChart) this.donutChart.destroy(); this.donutChart = new Chart(this.donutCanvas.nativeElement, { type: 'doughnut', data: { labels: labels.map((l) => l.replace('_', ' ')), datasets: [{ data, backgroundColor: colors, borderWidth: 2, borderColor: '#fff' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { padding: 16, font: { size: 12 } } } } } }); } // Bar chart if (this.barCanvas) { const amt = this.stats.amountByStatus || {}; const labels = Object.keys(amt); const data = Object.values(amt); const colors = labels.map((l) => statusColors[l] || '#999'); if (this.barChart) this.barChart.destroy(); this.barChart = new Chart(this.barCanvas.nativeElement, { type: 'bar', data: { labels: labels.map((l) => l.replace('_', ' ')), datasets: [{ label: 'Fraud Amount (₹)', data, backgroundColor: colors, borderRadius: 4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, ticks: { callback: (v: any) => '₹' + (v / 1000).toFixed(0) + 'K' } }, x: { ticks: { font: { size: 11 } } } } } }); } } formatCurrency(amount: number): string { return new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR', maximumFractionDigits: 0 }).format( amount ); } formatDate(dateStr: string): string { return new Date(dateStr).toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' }); } }