Files
cbsnexora/src/app/clerk/my-disbursement-requests/my-disbursement-requests.component.ts
T
Jaanu423andClaude Sonnet 4.6 e661161eac feat(clerk): enhance disbursement review and my-requests UI components
- Update disbursement review and my-disbursement-requests components
  with improved layout and additional UI enhancements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:07:01 +05:30

228 lines
6.2 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/.
*/
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { MatIconModule } from '@angular/material/icon';
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
import { MatNativeDateModule } from '@angular/material/core';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';
import { ClerkDisbursementService } from '../../cashier/clerk-disbursement.service';
import { SettingsService } from '../../settings/settings.service';
@Component({
selector: 'mifosx-my-disbursement-requests',
templateUrl: './my-disbursement-requests.component.html',
styleUrls: ['./my-disbursement-requests.component.scss'],
standalone: true,
imports: [
CommonModule,
FormsModule,
MatIconModule,
MatPaginatorModule,
MatNativeDateModule,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule
]
})
export class MyDisbursementRequestsComponent implements OnInit {
private service = inject(ClerkDisbursementService);
private router = inject(Router);
private settingsService = inject(SettingsService);
requests: any[] = [];
totalRecords = 0;
expandedIds = new Set<number>();
pageSize = 20;
pageIndex = 0;
pageSizeOptions = [
10,
20,
50
];
filterStatus = '';
fromDate: Date | null = null;
toDate: Date | null = null;
maxDate!: Date;
activePreset = 'today';
loading = false;
loaded = false;
readonly datePresets = [
{ key: 'today', label: 'Today' },
{ key: 'week', label: 'This Week' },
{ key: 'month', label: 'This Month' },
{ key: 'all', label: 'All' }
];
get statusOptions(): { value: string; label: string }[] {
return [
{ value: '', label: 'All' },
{ value: 'PENDING', label: 'Pending' },
{ value: 'APPROVED', label: 'Approved' },
{ value: 'REJECTED', label: 'Rejected' },
{ value: 'FAILED', label: 'Failed' }
];
}
ngOnInit(): void {
const today = new Date(this.settingsService.businessDate);
today.setHours(0, 0, 0, 0);
this.maxDate = today;
this.applyPreset('today');
}
load(): void {
this.loading = true;
this.service
.getMyRequests(
this.filterStatus || undefined,
this.pageIndex * this.pageSize,
this.pageSize,
this.isoDate(this.fromDate),
this.isoDate(this.toDate)
)
.pipe(catchError(() => of({ pageItems: [], totalFilteredRecords: 0 })))
.subscribe((res: any) => {
this.requests = res?.pageItems ?? [];
this.totalRecords = Number(res?.totalFilteredRecords ?? 0);
this.loading = false;
this.loaded = true;
});
}
onPageChange(event: PageEvent): void {
this.pageSize = event.pageSize;
this.pageIndex = event.pageIndex;
this.load();
}
onFilterChange(): void {
this.pageIndex = 0;
this.load();
}
onFromDateChange(): void {
this.activePreset = '';
if (this.fromDate && this.toDate && this.fromDate > this.toDate) {
this.toDate = new Date(this.fromDate);
}
this.pageIndex = 0;
this.load();
}
onToDateChange(): void {
this.activePreset = '';
this.pageIndex = 0;
this.load();
}
applyPreset(key: string): void {
this.activePreset = key;
const today = new Date(this.settingsService.businessDate);
today.setHours(0, 0, 0, 0);
if (key === 'today') {
this.fromDate = new Date(today);
this.toDate = new Date(today);
} else if (key === 'week') {
const start = new Date(today);
start.setDate(today.getDate() - today.getDay());
this.fromDate = start;
this.toDate = new Date(today);
} else if (key === 'month') {
this.fromDate = new Date(today.getFullYear(), today.getMonth(), 1);
this.toDate = new Date(today);
} else {
this.fromDate = null;
this.toDate = null;
}
this.pageIndex = 0;
this.load();
}
private isoDate(d: Date | null): string | undefined {
if (!d) return undefined;
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${y}-${m}-${day}`;
}
newDisbursement(): void {
this.router.navigate(['/clerk/dashboard']);
}
toggleExpand(id: number): void {
if (this.expandedIds.has(id)) {
this.expandedIds.delete(id);
} else {
this.expandedIds.add(id);
}
}
getCreditEntries(req: any): any[] {
try {
const arr = typeof req.credit_entries === 'string' ? JSON.parse(req.credit_entries) : req.credit_entries;
return Array.isArray(arr) ? arr.map((e: any) => ({ ...e, amount: parseFloat(e.amount) || 0 })) : [];
} catch {
return [];
}
}
creditCount(req: any): number {
return this.getCreditEntries(req).length;
}
statusClass(status: string): string {
const map: Record<string, string> = {
PENDING: 'mdr-status--pending',
PROCESSING: 'mdr-status--processing',
APPROVED: 'mdr-status--approved',
REJECTED: 'mdr-status--rejected',
FAILED: 'mdr-status--failed'
};
return map[status] ?? '';
}
statusLabel(status: string): string {
const map: Record<string, string> = {
PENDING: 'Pending Approval',
PROCESSING: 'Processing…',
APPROVED: 'Approved',
REJECTED: 'Rejected',
FAILED: 'Processing Failed'
};
return map[status] ?? status;
}
formatDateTime(val: any): string {
if (!val) return '—';
try {
const d = new Date(val);
return d.toLocaleString('en-IN', {
day: '2-digit',
month: 'short',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
} catch {
return String(val);
}
}
}