import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; import { SpinnerService } from '@core/services/spinner.service'; import { ReferenceFieldAPI } from '@core/typings/api/reference-fields.typing'; import { BaseApplication } from '@core/typings/application.typing'; import { ReferenceFieldsUI } from '@core/typings/ui/reference-fields.typing'; import { ApplicationAttachmentService } from '@features/application-view/application-attachments/application-attachments.service'; import { ClientSettingsService } from '@features/client-settings/client-settings.service'; import { FormDefinitionComponent } from '@features/configure-forms/form.typing'; import { ReferenceFieldsService } from '@features/reference-fields/services/reference-fields.service'; import { SimpleStringMap, TableDataDownloadFormat, TypeToken } from '@yourcause/common'; import { ModalFactory } from '@yourcause/common/modals'; import { isEqual } from 'lodash'; import { PopoverDirective } from 'ngx-bootstrap/popover'; import { Subscription } from 'rxjs'; import { ImportTableRowsModalComponent } from '../import-table-rows-modal/import-table-rows-modal.component'; import { TableFieldCrudModalComponent } from '../table-field-crud-modal/table-field-crud-modal.component'; @Component({ selector: 'gc-table-field', templateUrl: './gc-table-field.component.html', styleUrls: ['gc-table-field.component.scss'] }) export class GcTableFieldComponent implements OnInit, OnChanges, OnDestroy { @Input() data: ReferenceFieldsUI.TableResponseRowForUi[]; @Input() hideLabel: boolean; @Input() disabled: boolean; @Input() tabIndex: number; @Input() label: string; @Input() isFormBuilderView: boolean; @Input() description: string; @Input() rowsPerPage: number; @Input() parentFields: Partial; @Input() tooltipText: string; @Input() srOnlyLabel: boolean; @Input() field: ReferenceFieldAPI.ReferenceFieldDisplayModel; @Input() component: FormDefinitionComponent; @Input() translations: SimpleStringMap; @Input() notAutoSave: boolean; @Output() onDataChanged = new EventEmitter(); rows: ReferenceFieldsUI.TableResponseRowForUi[]; rowsForTable: ReferenceFieldsUI.TableResponseRowForUiMapped[]; visibleColumns: ReferenceFieldsUI.TableFieldForUi[]; sub = new Subscription(); FieldTypes = ReferenceFieldsUI.ReferenceFieldTypes; defaultCurrency = this.clientSettingsService.defaultCurrency; TableDataDownloadFormat = TableDataDownloadFormat; afterInit = false; $stringArray = new TypeToken(); constructor ( private referenceFieldService: ReferenceFieldsService, private modalFactory: ModalFactory, private clientSettingsService: ClientSettingsService, private spinnerService: SpinnerService, private applicationAttachmentService: ApplicationAttachmentService ) { /* This map holds the most recent value of rows after save */ /* The main reason we need this is when we create new rows */ /* This map will have the updated values for rowId */ this.sub.add( this.referenceFieldService.changesTo$('applicationFormTableRowsMap') .subscribe((value) => { if ( this.component && this.field && this.visibleColumns ) { if ( this.parentFields?.applicationFormId || this.parentFields?.revisionId ) { const key = this.referenceFieldService.getTableFormKey( this.parentFields.applicationFormId || this.parentFields.revisionId, this.field.referenceFieldId ); const updatedRows = value[key]; if ( updatedRows && !isEqual(updatedRows, this.rows) ) { this.setRowsFromData(updatedRows); this.emitRows(); } } } }) ); } async ngOnInit () { if (this.component) { await this.setVisibleColumns(); this.setRowsFromData(); } this.afterInit = true; } ngOnChanges () { if (this.afterInit) { this.setRowsFromData(); } } async setVisibleColumns () { await this.referenceFieldService.setColumnsForTable( this.field.referenceFieldId ); this.visibleColumns = this.referenceFieldService.getVisibleTableColumns( this.field.referenceFieldId, this.translations ); } setRowsFromData (data = this.data) { this.rows = [ ...(data || []) ]; this.rowsForTable = this.referenceFieldService.mapRowsForTable( this.rows, this.field.referenceFieldId ); } emitRows () { this.onDataChanged.emit(this.rows); } async addNewRecord () { const response = await this.modalFactory.open( TableFieldCrudModalComponent, { formId: this.parentFields?.formId, tableLabel: this.label, tableReferenceFieldId: this.field.referenceFieldId, applicationId: this.parentFields.applicationId, applicationFormId: this.parentFields.applicationFormId, notAutoSave: this.notAutoSave, translations: this.translations } ); if (response) { this.rows = [ ...this.rows, response.row ]; this.emitRows(); if (response.addAnother) { this.addNewRecord(); } } } async viewOrEditRecord ( row: ReferenceFieldsUI.TableResponseRowForUiMapped, index: number, isView: boolean ) { const response = await this.modalFactory.open( TableFieldCrudModalComponent, { tableLabel: this.label, tableReferenceFieldId: this.field.referenceFieldId, isView, formId: this.parentFields?.formId, currentRow: row, disableNextBtn: this.rows[index+1] ? false : true, disablePreviousBtn: this.rows[index-1] ? false : true, applicationId: this.parentFields.applicationId, applicationFormId: this.parentFields.applicationFormId, translations: this.translations } ); /* Edit Record */ if (response && !isView) { this.rows = [ ...this.rows.slice(0, index), response.row, ...this.rows.slice(index + 1) ]; this.emitRows(); } /* View Record */ if (response && isView) { /* Open Next Record */ if(response.openNext) { /* If the next record exists */ if(this.rowsForTable[index+1]) { this.viewOrEditRecord(this.rowsForTable[index+1], index+1, true); } } /* Open Previous Record */ if(response.openPrevious){ /* If the previous record exists */ if(this.rowsForTable[index-1]) { this.viewOrEditRecord(this.rowsForTable[index-1], index-1, true); } } } } openDeletePopover (popover: PopoverDirective) { setTimeout(() => { popover.show(); }); } deleteRecord (index: number) { this.rows = [ ...this.rows.slice(0, index), ...this.rows.slice(index + 1) ]; this.emitRows(); } async importToTable () { await this.modalFactory.open( ImportTableRowsModalComponent, { applicationId: this.parentFields.applicationId, applicationFormId: this.parentFields.applicationFormId, tableLabel: this.label, tableId: this.field.referenceFieldId, rowsForTable: this.referenceFieldService.mapRowsForTable( this.rows, this.field.referenceFieldId, true, true ), formId: this.parentFields.formId } ); } async handleOpenFile ( fileUrl: string ) { this.spinnerService.startSpinner(); await this.applicationAttachmentService.tryOpenReferenceFieldFromUrl(fileUrl); this.spinnerService.stopSpinner(); } downloadTable (format: TableDataDownloadFormat) { this.referenceFieldService.returnTableRowImportData( this.field.referenceFieldId, this.referenceFieldService.mapRowsForTable( this.rows, this.field.referenceFieldId, true, true ), true, format ); } ngOnDestroy () { this.sub.unsubscribe(); } }