feat(dds): upgrade DDS UI - agent, party, daily entry, approvals, product view
- Agent master: tabbed layout, customer search dialog, GL dropdown, account card, view page - Party master: tabbed layout, expected party code, nominee cards, relation from code values - Daily transaction entry: compact style, collections popup, Apply DDS Receipt placement - Approvals: new review modal, Agent Approval tab, DDS routing into product/pending account - DDS product: view page and list buttons; drop Auto Interest Post Day Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+175
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* 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 } from '@angular/core';
|
||||
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 { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { of } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
import { OrganizationService } from 'app/organization/organization.service';
|
||||
import { AuthenticationService } from 'app/core/authentication/authentication.service';
|
||||
import { DdsApprovalReviewComponent } from 'app/deposits/dds-approvals/dds-approval-review/dds-approval-review.component';
|
||||
import { ddsMadeOn } from '../../product-entities';
|
||||
|
||||
/**
|
||||
* Approvals › Agent Approval: every DDS agent request waiting on a checker - an agent being opened,
|
||||
* changed or closed.
|
||||
*
|
||||
* An agent has no voucher and no amount, so the money columns of DDS Approvals say nothing about one.
|
||||
* This lists them by what identifies an agent - who they are, which branch, which savings account the
|
||||
* commission goes into, how many guarantors - and opens the same review the DDS queue uses, which
|
||||
* shows the agent and their guarantors in full and approves or rejects through the DDS queue.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-agent-approvals',
|
||||
templateUrl: './agent-approvals.component.html',
|
||||
// The same look as Product Approval, so the tabs of this screen read alike.
|
||||
styleUrls: [
|
||||
'../pending-savings-products/pending-savings-products.component.scss',
|
||||
'./agent-approvals.component.scss'
|
||||
],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
FaIconComponent,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCellDef,
|
||||
MatHeaderCell,
|
||||
MatCellDef,
|
||||
MatCell,
|
||||
MatHeaderRowDef,
|
||||
MatHeaderRow,
|
||||
MatRowDef,
|
||||
MatRow,
|
||||
MatIcon
|
||||
]
|
||||
})
|
||||
export class AgentApprovalsComponent implements OnInit {
|
||||
private organizationService = inject(OrganizationService);
|
||||
private authenticationService = inject(AuthenticationService);
|
||||
private dialog = inject(MatDialog);
|
||||
|
||||
private readonly currentUserId = this.authenticationService.getCredentials()?.userId;
|
||||
|
||||
dataSource = new MatTableDataSource<any>([]);
|
||||
loading = true;
|
||||
|
||||
displayedColumns: string[] = [
|
||||
'request',
|
||||
'agent',
|
||||
'client',
|
||||
'branch',
|
||||
'savingAccount',
|
||||
'guarantors',
|
||||
'maker',
|
||||
'madeOn',
|
||||
'status',
|
||||
'actions'
|
||||
];
|
||||
|
||||
ngOnInit(): void {
|
||||
this.dataSource.filterPredicate = (row: any, filter: string) =>
|
||||
[
|
||||
row.agentName,
|
||||
row.agentCode,
|
||||
row.clientName,
|
||||
row.officeName,
|
||||
row.savingAccountNo,
|
||||
row.type,
|
||||
row.maker,
|
||||
row.makerName
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
.includes(filter);
|
||||
this.load();
|
||||
}
|
||||
|
||||
load(): void {
|
||||
this.loading = true;
|
||||
this.organizationService
|
||||
.getDdsApprovals('PENDING')
|
||||
.pipe(catchError(() => of([])))
|
||||
.subscribe((rows: any) => {
|
||||
const agents = (Array.isArray(rows) ? rows : []).filter((row: any) => row?.entity === 'AGENT');
|
||||
this.dataSource.data = agents.map((row: any) => ({ ...row, submittedOn: ddsMadeOn(row.madeOn) }));
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
applyFilter(value: string = ''): void {
|
||||
this.dataSource.filter = value.trim().toLowerCase();
|
||||
}
|
||||
|
||||
/** What the request is, as the badge reads it. */
|
||||
requestLabel(row: any): string {
|
||||
return row.action === 'CREATE' ? 'New' : row.action === 'CLOSE' ? 'Close' : 'Edit';
|
||||
}
|
||||
|
||||
isMine(row: any): boolean {
|
||||
return !!this.currentUserId && Number(row.makerId) === Number(this.currentUserId);
|
||||
}
|
||||
|
||||
formatDate(date: Date | null): string {
|
||||
return date ? date.toLocaleString('en-IN') : '';
|
||||
}
|
||||
|
||||
/** Opens the agent in full - the client, address, branch, accounts and guarantors the request carries. */
|
||||
view(row: any): void {
|
||||
this.dialog
|
||||
.open(DdsApprovalReviewComponent, {
|
||||
data: { entry: row, currentUserId: this.currentUserId },
|
||||
autoFocus: false
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe((done: boolean) => {
|
||||
if (done) {
|
||||
this.load();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this user may approve or reject the request. It is the same act the review and DDS
|
||||
* Approvals perform, through the DDS queue, which refuses whoever made the request - so it is not
|
||||
* offered to them here either.
|
||||
*/
|
||||
canAct(row: any): boolean {
|
||||
return !this.isMine(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve and Reject open the agent in full rather than acting at once: a request is never actioned
|
||||
* without its client, address, branch, accounts and guarantors on screen. The review carries the
|
||||
* Approve and Reject buttons, and asks before rejecting.
|
||||
*/
|
||||
approve(row: any): void {
|
||||
this.view(row);
|
||||
}
|
||||
|
||||
reject(row: any): void {
|
||||
this.view(row);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user