import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { SpinnerService } from '@core/services/spinner.service'; import { ReferenceFieldAPI } from '@core/typings/api/reference-fields.typing'; import { ReferenceFieldsUI } from '@core/typings/ui/reference-fields.typing'; import { ClientSettingsService } from '@features/client-settings/client-settings.service'; import { SimpleNumberMap, TypeaheadSelectOption } from '@yourcause/common'; import { ModalFactory } from '@yourcause/common/modals'; import { CreateEditReferenceFieldModalComponent } from '../create-edit-reference-field-modal/create-edit-reference-field-modal.component'; import { ReferenceFieldsService } from '../services/reference-fields.service'; @Component({ selector: 'gc-table-field-configuration', templateUrl: './table-field-configuration.component.html', styleUrls: ['./table-field-configuration.component.scss'] }) export class TableFieldConfigurationComponent implements OnInit { @Input() referenceFieldId: number; @Input() field: ReferenceFieldAPI.ReferenceFieldBaseModel; @Input() pendingKeyToBeCreated: string; @Input() isViewOnly = false; @Output() onTableFieldsChange = new EventEmitter(); @Output() onValidityChange = new EventEmitter(); @Output() onHideParentModal = new EventEmitter(); @Output() onNoImportAllowedChange = new EventEmitter(); rows: ReferenceFieldsUI.TableFieldForUi[] = []; availableFieldsMap: SimpleNumberMap[]>; typeMap = this.referenceFieldService.getReferenceFieldTypeToLabelMap(); RefFieldTypes = ReferenceFieldsUI.ReferenceFieldTypes; isValid: boolean; hasExistingFields = false; showImportNotAllowedAlert = false; constructor ( private referenceFieldService: ReferenceFieldsService, private modalFactory: ModalFactory, private spinnerService: SpinnerService, private clientSettingsService: ClientSettingsService ) { } get isDisabled () { return this.isViewOnly || this.isStandardProductField; } get refByIdMap () { return this.referenceFieldService.referenceFieldMapById; } get availableFields () { return this.referenceFieldService.getAvailableTableFields(); } get isStandardProductField () { if (this.clientSettingsService.clientSettings.isRootClient) { // Standard Product Field limitations do not apply to the root zone return false; } return this.field?.isStandardProductField ?? false; } async ngOnInit () { this.spinnerService.startSpinner(); const tableFields = await this.getTableFields(); this.rows = [ ...tableFields ]; this.setAvailableFieldsMapAndValidity(); this.emitTableFields(); this.spinnerService.stopSpinner(); } async getTableFields () { if (this.referenceFieldId) { return this.referenceFieldService.getTableFields( this.referenceFieldId ); } return []; } getBlankRow ( columnOrder: number, referenceFieldId?: number ): ReferenceFieldsUI.TableFieldForUi { const field = this.refByIdMap[referenceFieldId]; return { referenceFieldId: referenceFieldId || null, referenceFieldName: field?.name ?? '', label: field?.name ?? '', isRequired: false, showInTable: true, columnOrder, summarizeData: false, summarizeLabel: '', aggregateColumnReferenceFieldId: null, referenceField: field, errors: null }; } setAvailableFieldsMapAndValidity () { this.setShowImportNotAllowedAlert(); this.availableFieldsMap = this.referenceFieldService.getAvailableFieldsMap( this.rows, this.availableFields ); this.hasExistingFields = this.referenceFieldService.getExistingFields( this.rows, this.availableFields ); this.setValidity(); } emitTableFields () { this.onTableFieldsChange.emit(this.rows); } setShowImportNotAllowedAlert () { this.showImportNotAllowedAlert = this.rows.some((row) => { const isFile = row.referenceField?.type === ReferenceFieldsUI.ReferenceFieldTypes.FileUpload; const isDependentPicklist = row.referenceField?.parentReferenceFieldId && row.referenceField?.type === ReferenceFieldsUI.ReferenceFieldTypes.CustomDataTable; return isFile || isDependentPicklist; }); this.onNoImportAllowedChange.emit(this.showImportNotAllowedAlert); } setValidity () { let hasMissingParentPicklists = false; this.rows.forEach((row) => { const isChildPicklist = !!row.referenceField?.parentReferenceFieldId && row.referenceField?.type === ReferenceFieldsUI.ReferenceFieldTypes.CustomDataTable; if (isChildPicklist) { const parentExists = this.rows.some((_row) => { return _row.referenceFieldId === row.referenceField?.parentReferenceFieldId; }); if (!parentExists) { hasMissingParentPicklists = true; row.errors = { parentRequired: { i18nKey: 'common:toUseThisFieldAddParent', defaultValue: 'To use this field, you must also add the parent picklist __name__', context: { name: this.refByIdMap[ row.referenceField?.parentReferenceFieldId ].name } } }; } else { row.errors = null; } } else { row.errors = null; } }); this.isValid = this.rows.length > 0 && this.rows.some((row) => row.showInTable) && this.rows.every((row) => row.referenceFieldId) && !hasMissingParentPicklists; this.onValidityChange.emit(this.isValid); } addExistingField (referenceFieldId?: number) { const columnOrder = this.rows.length; this.rows = [ ...this.rows, this.getBlankRow(columnOrder, referenceFieldId) ]; this.emitTableFields(); this.setAvailableFieldsMapAndValidity(); } async createNewField () { this.onHideParentModal.emit(true); const response = await this.modalFactory.open( CreateEditReferenceFieldModalComponent, { forceIsTableField: true, pendingKeyToBeCreated: this.pendingKeyToBeCreated } ); if (response) { this.spinnerService.startSpinner(); const newField = await this.referenceFieldService.handleCreateOrUpdateField( null, response.field, response.tableFields, true ); if (newField) { this.addExistingField(newField.referenceFieldId); } this.spinnerService.stopSpinner(); } this.onHideParentModal.emit(false); } fieldChanged (row: ReferenceFieldsUI.TableFieldForUi) { if (row.referenceFieldId) { row.label = this.refByIdMap[row.referenceFieldId].name; row.referenceField = this.refByIdMap[row.referenceFieldId]; } else { row.label = ''; } row.showInTable = true; row.isRequired = false; row.summarizeData = false; row.summarizeLabel = ''; this.emitTableFields(); this.setAvailableFieldsMapAndValidity(); } summarizeDataChanged (row: ReferenceFieldsUI.TableFieldForUi) { if (row.summarizeData) { row.summarizeLabel = `${this.field.name} - ${row.label} - Total`; } else { row.summarizeLabel = ''; } this.emitTableFields(); } onDrop (rows: ReferenceFieldsUI.TableFieldForUi[]) { this.rows = rows; this.emitTableFields(); this.setAvailableFieldsMapAndValidity(); } removeRow (index: number) { this.rows = [ ...this.rows.slice(0, index), ...this.rows.slice(index + 1) ]; this.emitTableFields(); this.setAvailableFieldsMapAndValidity(); } }