Files
cbsnexora/src/app/home/dashboard/dashboard.component.ts
T
AdityaandClaude Opus 4.7 12bfc81219 feat: add 11 banking compliance & accounting report modules
Adds dialog components and feature modules for RBI/banking compliance,
KYC, and accounting reports. Wires them into the shell menu (RBI,
General Ledger, CKYC Mgmt, Others, Insurance) and dashboard cards.

Dialog components (lazy-loaded MatDialog):
- PSL Returns (Form III) — dashboard card + RBI menu
- ALM Statement of Structural Liquidity (Form ALM-1) — RBI menu
- CIMS / XBRL Returns (OSS-2 / PSA / RAL / RCP-UCB) — RBI menu, shared
  dialog with form-pill switcher
- CIC DQR (CIBIL / Experian / Equifax / CRIF, 72-col TUDF) — Others menu
  + dashboard card
- CKYC Compliance Reports (Re-KYC / Risk Review / CERSAI) — CKYC Mgmt
- Accounting Reports (P&L / Balance Sheet / Comparative B/S / GL DR/CR
  / Misc Expense Ledger) — General Ledger menu

Feature modules (Route.withShell):
- DEAF (/deaf): Form I, Form II, Annex 4, accounts register
- DICGC (/dicgc): Slab Report, SAHAY Depositor List
- FIU-IND (/fiu): FINnet 2.0 Statement of Financial Transactions
- SFT (/sft): Income Tax Section 285BA SFT
- CKYC Reports (/ckyc/ckyc-reports): standalone dashboard

UI:
- Theme-colored icons on 22 wired menu links across 5 menus —
  visually distinguishes wired (colored) from placeholder (gray) links.
- Backdrop classes per dialog in main.scss.

All using dummy data (Bharat Urban Bank Ltd, Karthik Kamath as Principal
Officer, dummy customer names) — no real bank or customer references.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 18:59:50 +05:30

202 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, inject } from '@angular/core';
import { UntypedFormControl, ReactiveFormsModule } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { activities } from '../activities';
import { MatAutocompleteTrigger, MatAutocomplete, MatOption } from '@angular/material/autocomplete';
import { MatDialog } from '@angular/material/dialog';
import { MatTooltipModule } from '@angular/material/tooltip';
import { AsyncPipe, DatePipe } from '@angular/common';
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
/**
* Dashboard component.
*/
@Component({
selector: 'mifosx-dashboard',
standalone: true,
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
MatAutocompleteTrigger,
MatAutocomplete,
MatTooltipModule,
AsyncPipe,
DatePipe,
FaIconComponent
]
})
export class DashboardComponent implements OnInit {
private router = inject(Router);
private dialog = inject(MatDialog);
today = new Date();
/** Array of all user activities */
userActivity: string[];
/** Array of most recent user activities */
recentActivities: string[];
/** Array of most frequent user activities */
frequentActivities: string[];
/** Search Text. */
searchText: UntypedFormControl = new UntypedFormControl();
/** Filtered Activities. */
filteredActivities: Observable<any[]>;
/** All User Activities. */
allActivities: any[] = activities;
/**
* Gets user activities from local storage.
*/
constructor() {
this.userActivity = JSON.parse(localStorage.getItem('mifosXLocation')) || [];
}
ngOnInit() {
this.recentActivities = this.getRecentActivities();
this.frequentActivities = this.getFrequentActivities();
this.setFilteredActivities();
}
/**
* Returns top eight recent activities.
*/
getRecentActivities() {
const reverseActivities = this.userActivity.reverse();
const uniqueActivities: string[] = [];
reverseActivities.forEach((activity: string) => {
if (!uniqueActivities.includes(activity)) {
uniqueActivities.push(activity);
}
});
const topEightRecentActivities = uniqueActivities
.filter(
(activity: string) => ![
'/',
'/login',
'/home',
'/dashboard'
].includes(activity)
)
.slice(0, 8);
return topEightRecentActivities;
}
/**
* Returns top eight frequent activities.
*/
getFrequentActivities() {
const frequencyCounts: any = {};
let index = this.userActivity?.length;
while (index) {
const activity = this.userActivity[--index];
frequencyCounts[activity] = (frequencyCounts[activity] || 0) + 1;
}
const frequencyCountsArray = Object.entries(frequencyCounts);
const topEigthFrequentActivities = frequencyCountsArray
.sort((a: any, b: any) => b[1] - a[1])
.map((entry: any[]) => entry[0])
.filter(
(activity: string) => ![
'/',
'/login',
'/home',
'/dashboard'
].includes(activity)
)
.slice(0, 8);
return topEigthFrequentActivities;
}
/**
* Navigates to the activity
*/
navigatetoActivity(activity: string) {
this.router.navigateByUrl(activity);
}
/**
* Sets filtered activities for autocomplete.
*/
setFilteredActivities() {
this.filteredActivities = this.searchText.valueChanges.pipe(
map((activity: any) => (typeof activity === 'string' ? activity : activity.activity)),
map((activityName: string) => (activityName ? this.filterActivity(activityName) : this.allActivities))
);
}
/**
* Filters activities.
* @param activityName Activity name to filter activity by.
* @returns {any} Filtered activities.
*/
private filterActivity(activityName: string): any {
const filterValue = activityName.toLowerCase();
return this.allActivities.filter((activity) => activity.activity.toLowerCase().indexOf(filterValue) === 0);
}
/**
* Opens the CRR/SLR details dialog with full liabilities breakdown.
*/
async openCrrSlrDialog(): Promise<void> {
const { CrrSlrDialogComponent } = await import('./crr-slr-dialog/crr-slr-dialog.component');
this.dialog.open(CrrSlrDialogComponent, {
width: '95vw',
maxWidth: '1100px',
maxHeight: '90vh',
panelClass: 'crr-slr-dialog-panel',
autoFocus: false
});
}
async openCrarDialog(): Promise<void> {
const { CrarDialogComponent } = await import('./crar-dialog/crar-dialog.component');
this.dialog.open(CrarDialogComponent, {
width: '95vw',
maxWidth: '1360px',
maxHeight: '90vh',
panelClass: 'crr-slr-dialog-panel',
autoFocus: false
});
}
async openCicDqrDialog(): Promise<void> {
const { CicDqrDialogComponent } = await import('./cic-dqr-dialog/cic-dqr-dialog.component');
this.dialog.open(CicDqrDialogComponent, {
width: '88vw',
maxWidth: '1200px',
maxHeight: '92vh',
panelClass: 'crr-slr-dialog-panel',
autoFocus: false,
disableClose: false,
hasBackdrop: true,
backdropClass: 'cic-dlg-backdrop'
});
}
async openPslReportDialog(): Promise<void> {
const { PslReportDialogComponent } = await import('./psl-report-dialog/psl-report-dialog.component');
this.dialog.open(PslReportDialogComponent, {
width: '92vw',
maxWidth: '1280px',
maxHeight: '92vh',
panelClass: 'crr-slr-dialog-panel',
autoFocus: false,
disableClose: false,
hasBackdrop: true,
backdropClass: 'psl-dlg-backdrop'
});
}
}