feat: add Pending Account and Reject Account tabs to Approval Center
- Added Pending Account tab showing savings/FD/RD/current accounts with status=pending (100) - Added Reject Account tab showing accounts with status=rejected (500) - Each tab displays: account no, client name, product, type badge, office, field officer, submitted/rejected date, status - Bulk approve with correct endpoint per deposit type (savings/FD/RD/current) - Resolvers use forkJoin to fetch savings + current accounts in parallel - Office name resolved via /offices map; field officer name taken directly from API response Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
3370a95235
commit
df96be9a0e
+178
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* 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 { SelectionModel } from '@angular/cdk/collections';
|
||||
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 { MatCheckbox } from '@angular/material/checkbox';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
|
||||
import { TasksService } from '../../tasks.service';
|
||||
import { SettingsService } from 'app/settings/settings.service';
|
||||
import { Dates } from 'app/core/utils/dates';
|
||||
import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.component';
|
||||
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
|
||||
import { DatepickerBase } from 'app/shared/form-dialog/formfield/model/datepicker-base';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-pending-account',
|
||||
templateUrl: './pending-account.component.html',
|
||||
styleUrls: ['./pending-account.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FaIconComponent,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCheckbox,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
MatIcon
|
||||
]
|
||||
})
|
||||
export class PendingAccountComponent {
|
||||
private route = inject(ActivatedRoute);
|
||||
private router = inject(Router);
|
||||
private dialog = inject(MatDialog);
|
||||
private dateUtils = inject(Dates);
|
||||
private settingsService = inject(SettingsService);
|
||||
private tasksService = inject(TasksService);
|
||||
|
||||
dataSource: MatTableDataSource<any>;
|
||||
selection: SelectionModel<any>;
|
||||
showData = false;
|
||||
|
||||
displayedColumns: string[] = [
|
||||
'select',
|
||||
'accountNo',
|
||||
'clientName',
|
||||
'productName',
|
||||
'accountType',
|
||||
'officeName',
|
||||
'fieldOfficerName',
|
||||
'submittedOnDate',
|
||||
'status'
|
||||
];
|
||||
|
||||
constructor() {
|
||||
this.route.data.subscribe((data: { pendingAccountsData: any }) => {
|
||||
// status 100 = submitted and pending approval only; exclude active/approved/etc.
|
||||
const items = (data.pendingAccountsData?.pageItems || []).filter((a: any) => a.status?.id === 100);
|
||||
this.showData = items.length > 0;
|
||||
this.dataSource = new MatTableDataSource(items);
|
||||
this.selection = new SelectionModel(true, []);
|
||||
});
|
||||
}
|
||||
|
||||
isAllSelected() {
|
||||
return this.selection.selected.length === this.dataSource.data.length;
|
||||
}
|
||||
|
||||
masterToggle() {
|
||||
this.isAllSelected() ? this.selection.clear() : this.dataSource.data.forEach((row) => this.selection.select(row));
|
||||
}
|
||||
|
||||
checkboxLabel(row?: any): string {
|
||||
if (!row) return `${this.isAllSelected() ? 'deselect' : 'select'} all`;
|
||||
return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row`;
|
||||
}
|
||||
|
||||
applyFilter(filterValue: string = '') {
|
||||
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||
}
|
||||
|
||||
approveAccounts() {
|
||||
const formfields: FormfieldBase[] = [
|
||||
new DatepickerBase({
|
||||
controlName: 'approvedOnDate',
|
||||
label: 'Approved On Date',
|
||||
value: new Date(),
|
||||
type: 'datetime-local',
|
||||
required: true
|
||||
})
|
||||
];
|
||||
const dialogRef = this.dialog.open(FormDialogComponent, {
|
||||
data: {
|
||||
title: 'Enter Approval Date',
|
||||
layout: { addButtonText: 'Confirm' },
|
||||
formfields
|
||||
}
|
||||
});
|
||||
dialogRef.afterClosed().subscribe((response: any) => {
|
||||
if (response?.data) {
|
||||
this.bulkApprove(response.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private bulkApprove(submittedData: any) {
|
||||
const dateFormat = this.settingsService.dateFormat;
|
||||
const locale = this.settingsService.language.code;
|
||||
const approvedOnDate = this.dateUtils.formatDate(submittedData.value.approvedOnDate, dateFormat);
|
||||
const payload = { approvedOnDate, dateFormat, locale };
|
||||
|
||||
const selected = this.selection.selected;
|
||||
let done = 0;
|
||||
selected.forEach((account: any) => {
|
||||
const depositTypeId = account.depositType?.id;
|
||||
let approve$;
|
||||
if (account.accountType === 'Current') {
|
||||
approve$ = this.tasksService.approveCurrentAccount(account.id, payload);
|
||||
} else if (depositTypeId === 200) {
|
||||
approve$ = this.tasksService.approveFDAccount(account.id, payload);
|
||||
} else if (depositTypeId === 300) {
|
||||
approve$ = this.tasksService.approveRDAccount(account.id, payload);
|
||||
} else {
|
||||
approve$ = this.tasksService.approveSavingsAccount(account.id, payload);
|
||||
}
|
||||
approve$.subscribe(() => {
|
||||
done++;
|
||||
if (done === selected.length) {
|
||||
this.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
formatDate(date: any): string {
|
||||
if (!date) return '';
|
||||
if (Array.isArray(date)) {
|
||||
const d = new Date(date[0], date[1] - 1, date[2]);
|
||||
return d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
reload() {
|
||||
const url = this.router.url;
|
||||
this.router
|
||||
.navigateByUrl('/checker-inbox-and-tasks', { skipLocationChange: true })
|
||||
.then(() => this.router.navigate([url]));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user