/** * 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, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { MatTableDataSource, MatTable, MatColumnDef, MatHeaderCellDef, MatHeaderCell, MatCellDef, MatCell, MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow } from '@angular/material/table'; import { MatDialog } from '@angular/material/dialog'; import { MatIcon } from '@angular/material/icon'; import { MatIconButton } from '@angular/material/button'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { SavingsService } from 'app/savings/savings.service'; import { CurrentAccountsService } from 'app/current-accounts/current-accounts.service'; import { ConfirmationDialogComponent } from 'app/shared/confirmation-dialog/confirmation-dialog.component'; import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component'; import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base'; import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; /** * Checker-facing list of savings account Nominee / Digital-Feature edits awaiting approval. * Mirrors PendingSavingsProductsComponent's list + compare + approve/reject pattern. */ @Component({ selector: 'mifosx-pending-savings-account-edits', templateUrl: './pending-savings-account-edits.component.html', styleUrls: ['./pending-savings-account-edits.component.scss'], imports: [ ...STANDALONE_SHARED_IMPORTS, FaIconComponent, MatTable, MatColumnDef, MatHeaderCellDef, MatHeaderCell, MatCellDef, MatCell, MatHeaderRowDef, MatHeaderRow, MatRowDef, MatRow, MatIcon, MatIconButton ] }) export class PendingSavingsAccountEditsComponent { private route = inject(ActivatedRoute); private router = inject(Router); private dialog = inject(MatDialog); private savingsService = inject(SavingsService); private currentAccountsService = inject(CurrentAccountsService); dataSource: MatTableDataSource; showData = false; displayedColumns: string[] = [ 'accountNo', 'accountType', 'makerUsername', 'createdOn', 'holdAmount', 'status', 'actions' ]; selectedEdit: any = null; constructor() { this.route.data.subscribe((data: { pendingSavingsAccountEditsData: any; pendingCurrentAccountEditsData: any }) => { const savingsItems = (data.pendingSavingsAccountEditsData || []).map((item: any) => { let type = 'Savings'; if (item.depositAccountType === 'FIXED_DEPOSIT') { type = 'Fixed Deposit'; } else if (item.depositAccountType === 'RECURRING_DEPOSIT') { type = 'Recurring Deposit'; } return { ...item, accountType: type }; }); const currentItems = (data.pendingCurrentAccountEditsData || []).map((item: any) => ({ ...item, accountType: 'Current' })); const items = [ ...savingsItems, ...currentItems ]; this.showData = items.length > 0; this.dataSource = new MatTableDataSource(items); }); } applyFilter(filterValue: string = '') { this.dataSource.filter = filterValue.trim().toLowerCase(); } formatDate(date: any): string { if (!date) return ''; const d = Array.isArray(date) ? new Date(date[0], date[1] - 1, date[2], date[3] || 0, date[4] || 0) : new Date(date); return isNaN(d.getTime()) ? String(date) : d.toLocaleString('en-IN'); } /** Readable list of "key: value" pairs for the proposed facility/nominee changes. */ entries(obj: any): { key: string; value: any }[] { if (!obj) return []; return Object.keys(obj).map((key) => ({ key, value: obj[key] })); } viewCompare(edit: any): void { this.selectedEdit = this.selectedEdit === edit ? null : edit; } closeCompare(): void { this.selectedEdit = null; } approve(edit: any): void { const isCurrent = edit.accountType === 'Current'; const dialogRef = this.dialog.open(ConfirmationDialogComponent, { data: { heading: isCurrent ? 'Approve Current Account Edit' : 'Approve Savings Account Edit', dialogContext: `Apply the pending Nominee/Digital-Feature edit for account "${edit.accountNo}"? This cannot be undone.`, type: 'basic' } }); dialogRef.afterClosed().subscribe((response: any) => { if (response?.confirm) { if (isCurrent) { this.currentAccountsService.approveCurrentAccountEdit(edit.currentAccountId).subscribe(() => this.reload()); } else { this.savingsService.approveSavingsAccountEdit(edit.savingsAccountId).subscribe(() => this.reload()); } } }); } reject(edit: any): void { const isCurrent = edit.accountType === 'Current'; const formfields: FormfieldBase[] = [ new InputBase({ controlName: 'rejectReason', label: 'Reason for rejection', value: '', type: 'text', required: false }) ]; const dialogRef = this.dialog.open(FormDialogComponent, { data: { title: `Reject edit for account "${edit.accountNo}"`, layout: { addButtonText: 'Reject' }, formfields } }); dialogRef.afterClosed().subscribe((response: any) => { if (response?.data) { const rejectReason = response.data.value.rejectReason || ''; if (isCurrent) { this.currentAccountsService .rejectCurrentAccountEdit(edit.currentAccountId, rejectReason) .subscribe(() => this.reload()); } else { this.savingsService .rejectSavingsAccountEdit(edit.savingsAccountId, rejectReason) .subscribe(() => this.reload()); } } }); } reload(): void { this.closeCompare(); const url = this.router.url; this.router .navigateByUrl('/checker-inbox-and-tasks', { skipLocationChange: true }) .then(() => this.router.navigate([url])); } }