import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { SpinnerService } from '@core/services/spinner.service'; import { ReferenceFieldAPI } from '@core/typings/api/reference-fields.typing'; import { FormAudience } from '@features/configure-forms/form.typing'; import { FileService, SelectOption, TypeSafeFormBuilder, TypeSafeFormGroup } from '@yourcause/common'; import { AnalyticsService, EventType } from '@yourcause/common/analytics'; import { I18nService } from '@yourcause/common/i18n'; import { YCModalComponent } from '@yourcause/common/modals'; import { Subscription } from 'rxjs'; import { ReferenceFieldsService } from '../services/reference-fields.service'; type ModalReturn = ReferenceFieldAPI.MergeFormFieldsApi; interface DataPreservationFormGroup { field1: number; field2: number; } interface FieldStat { formCount: number; appCount: number; formNames: string[]; } @Component({ selector: 'gc-merge-fields-modal', styleUrls: ['./merge-fields-modal.component.scss'], templateUrl: './merge-fields-modal.component.html' }) export class MergeFieldsModalComponent extends YCModalComponent implements OnInit, OnDestroy { @Input() fieldsToMerge: ReferenceFieldAPI.ReferenceFieldDisplayModel[]; // always 2 fields @Input() fieldDetails: ReferenceFieldAPI.ReferenceFieldDetail[]; // detail of 2 fields existingReferenceField: ReferenceFieldAPI.ReferenceFieldDisplayModel; uniqueKeys: string[]; existingKeys: string[]; saving = false; field: ReferenceFieldAPI.ReferenceFieldBaseModel; isValid = false; addingCategory: boolean; isConfirmStep = false; skipConfirmStep = false; // For manager fields and when no responses exist formGroup: TypeSafeFormGroup; field1Options: SelectOption[] = []; field2Options: SelectOption[] = []; field1Stats: FieldStat; field2Stats: FieldStat; responses: ReferenceFieldAPI.ApplicationResponse[]; sub = new Subscription(); constructor ( private referenceFieldService: ReferenceFieldsService, private spinnerService: SpinnerService, private formBuilder: TypeSafeFormBuilder, private fileService: FileService, private i18n: I18nService, private analyticsService: AnalyticsService ) { super(); } async ngOnInit () { this.spinnerService.startSpinner(); this.existingReferenceField = this.referenceFieldService.convertMergeFieldsToOneField( this.fieldsToMerge ); await this.setApplicationResponses(); this.setUniqueKeys(); this.setFormGroupAndOptions(); this.setFieldStats(); this.spinnerService.stopSpinner(); } async setApplicationResponses () { this.responses = await this.referenceFieldService.getApplicationResponsesForMerge( this.fieldsToMerge[0].referenceFieldId, this.fieldsToMerge[1].referenceFieldId ); } setFormGroupAndOptions () { this.field1Options = [{ display: this.fieldsToMerge[0].name, value: this.fieldsToMerge[0].referenceFieldId }]; this.field2Options = [{ display: this.fieldsToMerge[1].name, value: this.fieldsToMerge[1].referenceFieldId }]; this.formGroup = this.formBuilder.group({ field1: null, field2: null }); // Only one can be selected at a time this.sub.add(this.formGroup.get('field1').valueChanges.subscribe((val) => { if (val && this.formGroup.value.field2) { this.formGroup.get('field2').setValue(null); } })); this.sub.add(this.formGroup.get('field2').valueChanges.subscribe((val) => { if (val && this.formGroup.value.field1) { this.formGroup.get('field1').setValue(null); } })); } setFieldStats () { this.field1Stats = { formCount: this.fieldsToMerge[0].formCount, appCount: this.responses.reduce((acc, response) => { return response.referenceField1Response ? acc + 1 : acc; }, 0), formNames: this.fieldDetails[0].forms.map((form) => { return form.name; }) }; this.field2Stats = { formCount: this.fieldsToMerge[1].formCount, appCount: this.responses.reduce((acc, response) => { return response.referenceField2Response ? acc + 1 : acc; }, 0), formNames: this.fieldDetails[1].forms.map((form) => { return form.name; }) }; this.skipConfirmStep = this.fieldsToMerge[0].formAudience === FormAudience.MANAGER || (this.field1Stats.appCount === 0 && this.field2Stats.appCount === 0); } setUniqueKeys () { this.uniqueKeys = this.referenceFieldService.allReferenceFields.map((field) => { return (field.key || '').toLowerCase(); }); this.existingKeys = this.fieldsToMerge.map((field) => field.key); } onPrimaryClick () { if (this.isConfirmStep || this.skipConfirmStep) { this.save(); this.analyticsService.emitEvent({ eventName: 'Save merge fields', eventType: EventType.Click, extras: null }); } else { this.isConfirmStep = true; this.analyticsService.emitEvent({ eventName: 'Go to confirm merge fields', eventType: EventType.Click, extras: null }); } } downloadResponses () { const mapped = this.responses.map((res) => { const field1Header = this.i18n.translate( 'common:textFieldResponseDynamic', { fieldName: this.fieldsToMerge[0].name }, '__fieldName__ Response' ); let field2Header = this.i18n.translate( 'common:textFieldResponseDynamic', { fieldName: this.fieldsToMerge[1].name }, '__fieldName__ Response' ); if (field1Header === field2Header) { field2Header = field2Header + ' 2'; } return { 'Application ID': res.applicationId, [field1Header]: res.referenceField1Response ?? '', [field2Header]: res.referenceField2Response ?? '' }; }); const csv = this.fileService.convertObjectArrayToCSVString( mapped ); this.fileService.downloadString(csv, 'text/csv', 'Application_Response_Download.csv'); } async save () { this.saving = true; this.spinnerService.startSpinner(); const formVal = this.formGroup.value; let priorityReferenceFieldId = this.fieldsToMerge[0].referenceFieldId; if (!!formVal.field2) { priorityReferenceFieldId = formVal.field2; } const res = await this.referenceFieldService.formatMergeModalReturn( this.field, this.fieldsToMerge[0].referenceFieldId, this.fieldsToMerge[1].referenceFieldId, priorityReferenceFieldId, this.addingCategory ); this.spinnerService.stopSpinner(); this.closeModal.emit(res); this.saving = false; } ngOnDestroy () { this.sub.unsubscribe(); } }