Add customer search dialog and sponsor DD/reconciliation modules
Adds sponsor-dd and sponsor-dd-reconciliation feature modules, a shared customer search dialog, and wires them into routing/shell/ organization screens alongside cashier and deposit-request updates.
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* 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 { CommonModule } from '@angular/common';
|
||||
import { Router, RouterModule } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { SponsorDDReconciliationService } from '../sponsor-dd-reconciliation.service';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-reco-batch-upload',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule,
|
||||
FormsModule,
|
||||
MatCardModule,
|
||||
MatButtonModule,
|
||||
MatSnackBarModule,
|
||||
MatProgressBarModule,
|
||||
FaIconComponent
|
||||
],
|
||||
templateUrl: './reco-batch-upload.component.html',
|
||||
styles: [
|
||||
`
|
||||
.upload-container {
|
||||
padding: 24px;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.header-row {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.header-row h2 {
|
||||
margin: 0 0 4px 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
.header-row p {
|
||||
margin: 0;
|
||||
color: #64748b;
|
||||
font-size: 14px;
|
||||
}
|
||||
.mat-mdc-card {
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
padding: 32px;
|
||||
}
|
||||
.dropzone {
|
||||
border: 2px dashed #cbd5e1;
|
||||
border-radius: 8px;
|
||||
padding: 40px 20px;
|
||||
text-align: center;
|
||||
background-color: #f8fafc;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
background-color 0.2s;
|
||||
}
|
||||
.dropzone:hover {
|
||||
border-color: #7c3aed;
|
||||
background-color: #f1f5f9;
|
||||
}
|
||||
.dropzone-icon {
|
||||
font-size: 48px;
|
||||
color: #94a3b8;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.file-details {
|
||||
margin-top: 24px;
|
||||
padding: 16px;
|
||||
background-color: #f1f5f9;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.file-name {
|
||||
font-weight: 500;
|
||||
color: #334155;
|
||||
}
|
||||
.file-size {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
.progress-section {
|
||||
margin-top: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
.progress-text {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
margin-top: 8px;
|
||||
}
|
||||
`
|
||||
]
|
||||
})
|
||||
export class RecoBatchUploadComponent {
|
||||
private recoService = inject(SponsorDDReconciliationService);
|
||||
private router = inject(Router);
|
||||
private snackBar = inject(MatSnackBar);
|
||||
|
||||
selectedFile: File | null = null;
|
||||
loading = false;
|
||||
progressMessage = '';
|
||||
|
||||
onFileSelected(event: any): void {
|
||||
const file = event.target.files[0];
|
||||
if (file) {
|
||||
this.selectedFile = file;
|
||||
}
|
||||
}
|
||||
|
||||
triggerFileSelect(fileInput: HTMLInputElement): void {
|
||||
fileInput.click();
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
if (!this.selectedFile) return;
|
||||
|
||||
this.loading = true;
|
||||
this.progressMessage = 'Reading file and preparing upload payload...';
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
const fileContent = reader.result as string;
|
||||
this.progressMessage = 'Uploading clearing scroll for processing...';
|
||||
|
||||
this.recoService
|
||||
.uploadClearingScroll({
|
||||
fileName: this.selectedFile!.name,
|
||||
fileContent: fileContent
|
||||
})
|
||||
.subscribe({
|
||||
next: (res) => {
|
||||
this.snackBar.open(
|
||||
`Reconciliation batch processed successfully. ${res.successCount} matched, ${res.mismatchCount} exceptions.`,
|
||||
'Close',
|
||||
{ duration: 7000 }
|
||||
);
|
||||
this.router.navigate([
|
||||
'/sponsor-dd-reconciliation/batches',
|
||||
res.id
|
||||
]);
|
||||
},
|
||||
error: (err) => {
|
||||
this.loading = false;
|
||||
this.progressMessage = '';
|
||||
this.snackBar.open('Reconciliation failed: ' + (err.error?.message || err.message), 'Close', {
|
||||
duration: 6000
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
reader.readAsDataURL(this.selectedFile); // Reads as Base64 Data URL, which our backend decodes safely.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user