/** * 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. } }