import { HttpErrorResponse } from '@angular/common/http'; import { AfterViewInit, Component, OnDestroy } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { SpinnerService } from '@core/services/spinner.service'; import { APIDataImport } from '@core/typings/api/data-import.typing'; import { HistoricalDataImportModelInstance } from '@core/typings/data-import.typing'; import { AddFileToBatchModalComponent } from '@features/platform-admin/historical-imports/add-file-to-batch-modal/add-file-to-batch-modal.component'; import { APIResult, DebounceFactory, InflectService, PaginationOptions, Tab, TopLevelFilter, YcFile } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { LogService } from '@yourcause/common/logging'; import { ConfirmationModalComponent, ModalFactory } from '@yourcause/common/modals'; import { NotifierService } from '@yourcause/common/notifier'; import { map } from 'rxjs'; import { HistoricalImportResources } from '../historical-import.resources'; import { HistoricalImportService } from '../historical-import.service'; import { ViewBatchErrorsModalComponent } from '../view-batch-errors-modal/view-batch-errors-modal.component'; @Component({ selector: 'gc-view-batch', templateUrl: './view-batch.component.html', styleUrls: ['./view-batch.component.scss'] }) export class ViewBatchComponent implements OnDestroy, AfterViewInit { private readonly id = this.activatedRoute.snapshot.params.id; private type = this.inflect.dashCaseToPascalCase( this.activatedRoute.snapshot.params.type ) as keyof typeof APIDataImport.DataImportFileType; private model = this.historicalImportService.getModelByModelType(this.type); private readonly type$ = this.activatedRoute.params.pipe( map(types => types.type)); tableDataFactory = DebounceFactory .createSimple, PaginationOptions>( (paginationOptions) => this.historicalImportResources.getRowsInBatch( this.id, this.type, paginationOptions ) ); private readonly typeSub = this.type$.subscribe(type => { type = this.inflect.dashCaseToPascalCase(type) as keyof typeof APIDataImport.DataImportFileType; if (this.type !== type) { this.columns = null; this.columnKeys = null; setTimeout(() => { this.type = type; this.model = this.historicalImportService.getModelByModelType(this.type); this.setColumns(); this.tableDataFactory.reset.emit(); }); } }); topLevelFilters = [ new TopLevelFilter( 'text', 'importId', '', this.i18n.translate( 'historical:textFilterByImportId', {}, 'Filter by import ID' ) ) ]; columns: string[]; columnKeys: string[]; tabs: Tab[] = [{ link: '../applicants', label: 'Applicants', labelKey: 'common:lblApplicants' }, { link: '../applications', label: 'Applications', labelKey: 'common:hdrApplications' }, { link: '../application-in-kind-items', label: 'Items requested', labelKey: 'common:hdrItemsRequested' }, { link: '../application-forms', label: 'Application Forms', labelKey: 'GLOBAL:textApplicationForms' }, { link: '../application-reference-fields', label: 'Form Responses', labelKey: 'common:hdrFormResponses' }, { link: '../awards', label: 'Awards', labelKey: 'common:hdrAwards' }, { link: '../award-in-kind-items', label: 'Items awarded', labelKey: 'common:hdrItemsAwarded' }, { link: '../organizations', label: 'Organizations', labelKey: 'common:lblOrganizations' }, { link: '../payments', label: 'Payments', labelKey: 'common:hdrPayments' }, { link: '../payment-in-kind-items', label: 'Items paid', labelKey: 'common:hdrItemsPaid' }, { link: '../employee-applicant-info', label: 'Employee Applicant Info', labelKey: 'common:hdrEmployeeApplicantInfo' }]; currentBatchName = this.historicalImportService.currentBatch.name; currentTableType$ = this.activatedRoute.url; constructor ( private logger: LogService, private notifier: NotifierService, private router: Router, private spinnerService: SpinnerService, private i18n: I18nService, private inflect: InflectService, private modalFactory: ModalFactory, private activatedRoute: ActivatedRoute, private historicalImportResources: HistoricalImportResources, private historicalImportService: HistoricalImportService ) { this.setColumns(); this.tableDataFactory.reset.emit(); } ngAfterViewInit () { this.tableDataFactory.reset.emit(); } private setColumns () { const columns = this.historicalImportService.getAttrs(this.model); this.columnKeys = columns.map(attr => `historical:hdr${this.inflect.pascalize(attr)}`); this.columns = columns.map(column => column[0].toLowerCase() + column.slice(1)); } async validateBatch () { this.spinnerService.startSpinner(); const errors = await this.historicalImportService.getValidationErrors(+this.id); this.spinnerService.stopSpinner(); await this.modalFactory.open(ViewBatchErrorsModalComponent, { errors }); } async processBatch () { const proceed = await this.modalFactory.open( ConfirmationModalComponent, { confirmButtonText: this.i18n.translate('common:textYes'), confirmText: this.i18n.translate( 'GLOBAL:textProcessBatchConfirmText', {}, 'You are about to process this batch. Once it has been processed, you will not be able to view the contents. Do you want to continue?' ), modalHeader: this.i18n.translate('historical:hdrProcessBatch') } ); if (proceed) { this.spinnerService.startSpinner(); try { await this.historicalImportResources.processBatch(this.id); this.router.navigate([ 'platform/historical-imports/view-batches' ]); } catch (err) { this.logger.error(err); this.notifier.error(this.i18n.translate( 'historical:notificationErrorProcessingBatch', {}, 'There was an error processing your batch, please check your data and try again' )); } this.spinnerService.stopSpinner(); } } async addFile () { const res = await this.modalFactory.open( AddFileToBatchModalComponent, { initialModel: this.model }, { class: 'modal-full-size' } ); if (res) { this.spinnerService.startSpinner(); this.spinnerService.setLoadingMessage(this.i18n.translate( 'GLOBAL:textDoNotCloseOrRefreshThisPage', {}, 'Do not close or refresh this page until complete' )); try { const primaryBatchId = await this.historicalImportService.addFileToBatch( +this.id, res.fileType, res.file, res.timezone, (res.file as YcFile).fileName ?? '', res.paymentBatchName ); await this.historicalImportService.commitFileBatch(primaryBatchId, this.id); let ancillaryBatchId: number; // if the user is uploading form responses // the modal emits an extra file that includes the Form Field responses // as well as a file that has the ApplicationForms if (res.fileType === APIDataImport.DataImportFileType.ApplicationForms) { ancillaryBatchId = await this.historicalImportService.addFileToBatch( +this.id, APIDataImport.DataImportFileType.ApplicationReferenceFields, res.ancillaryFile, res.timezone, (res.file as YcFile).fileName ?? '' ); await this.historicalImportService.commitFileBatch(ancillaryBatchId, this.id); } this.tableDataFactory.reset.emit(); } catch (err) { const e = err as HttpErrorResponse; this.logger.error(e); // if error we need to parse for message let error: any; try { error = e.error ? JSON.parse(e.error) : e; } catch { } if (error?.message) { this.notifier.error(error.message); } else { this.notifier.error(this.i18n.translate( 'historical:textErrorUploadingFile', {}, 'There was an error uploading your file' )); } } this.spinnerService.stopSpinner(); this.spinnerService.setLoadingMessage(''); } } ngOnDestroy () { this.typeSub.unsubscribe(); } }