import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; import { Validators } from '@angular/forms'; import { FormBuilderFactoryService } from '@core/services/form-builder-factory.service'; import { SpinnerService } from '@core/services/spinner.service'; import { BaseApplication } from '@core/typings/application.typing'; import { MasterResponse } from '@core/typings/program.typing'; import { ReferenceFieldsUI } from '@core/typings/ui/reference-fields.typing'; import { Form, FormData, FormDefinitionForUi, FormioChangesWithCompKey, FormTypes } from '@features/configure-forms/form.typing'; import { FormsService } from '@features/configure-forms/services/forms/forms.service'; import { ComponentHelperService } from '@features/formio/services/component-helper/component-helper.service'; import { FormHelperService } from '@features/formio/services/form-helper/form-helper.service'; import { FormLogicService } from '@features/formio/services/form-logic/form-logic.service'; import { ArrayHelpersService, PanelTypes, TypeaheadSelectOption, TypeSafeFormBuilder, TypeSafeFormGroup } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { isEqual } from 'lodash'; import { Subscription } from 'rxjs'; import { ProgramService } from '../program.service'; interface SuccessFailGroup { success: string; fail: string; } interface ProgramFormGroup { eligibility: number; defaultForm: number; notifyNominators: boolean; } @Component({ selector: 'gc-program-forms', templateUrl: './program-forms.component.html', styleUrls: ['./program-forms.component.scss'], encapsulation: ViewEncapsulation.None }) export class ProgramFormsComponent implements OnInit, OnDestroy { formGroup: TypeSafeFormGroup; customMessagesForm: TypeSafeFormGroup; successMessage = this.programService.passMessage; failMessage = this.programService.failMessage; PanelTypes = PanelTypes; masterForm: Form; formData: FormData; sub = new Subscription(); defaultSelectEligibility = this.i18n.translate( 'PROGRAM:textSelectEligibilityForm', {}, 'Select an eligibility form for this program' ); defaultSelectRequest = this.i18n.translate( 'PROGRAM:textSelectDefaultForm', {}, 'Select a default form for this program' ); defaultSelectNomination = this.i18n.translate( 'PROGRAM:textSelectNominationForm', {}, 'Select a nomination form for this program' ); eligibilityOptions: TypeaheadSelectOption[] = []; requestFormOptions: TypeaheadSelectOption[] = []; nominationFormOptions: TypeaheadSelectOption[] = []; formDefinition: FormDefinitionForUi[]; defaultFormChangedAlert = ''; showEligibilityForm = true; parentFields: Partial; ready = false; constructor ( private formService: FormsService, private formBuilder: TypeSafeFormBuilder, private i18n: I18nService, private spinnerService: SpinnerService, private arrayHelperService: ArrayHelpersService, private programService: ProgramService, private formBuilderFactory: FormBuilderFactoryService, private formHelperService: FormHelperService, private componentHelper: ComponentHelperService, private formLogicService: FormLogicService ) { this.sub.add(this.programService.changesTo$('configureProgramMap').subscribe(() => { this.updateValidation(); })); } get isNomination () { return location.pathname.includes('nomination'); } get programMap () { return this.programService.get('configureProgramMap'); } get activeProgramId () { return this.programService.get('activeProgramId'); } get program () { return this.programMap[this.activeProgramId]; } get eligibilityRevisionId (): number { if (this.eligibilityFormId) { const found = this.formService.published.find((form) => { return +form.formId === +this.eligibilityFormId; }); return found ? found.revisionId : null; } return null; } get eligibilityFormId () { return this.formGroup.value.eligibility; } async ngOnInit () { const forms = this.formService.published; this.nominationFormOptions = this.arrayHelperService.sort( forms.filter((form) => form.formType === FormTypes.NOMINATION) .map((form) => { return { label: form.name, value: form.formId }; }), 'label' ); this.requestFormOptions = this.arrayHelperService.sort( forms.filter((form) => form.formType === FormTypes.REQUEST) .map((form) => { return { label: form.name, value: form.formId }; }), 'label' ); this.eligibilityOptions = this.arrayHelperService.sort( forms.filter((form) => form.formType === FormTypes.ELIGIBILITY) .map((form) => { return { label: form.name, value: form.formId }; }), 'label' ); this.nominationFormOptions = this.arrayHelperService.sort( forms.filter((form) => form.formType === FormTypes.NOMINATION) .map((form) => { return { label: form.name, value: form.formId }; }), 'label' ); this.formGroup = this.formBuilder.group({ eligibility: [this.program.eligibilityForm], defaultForm: [this.program.defaultForm, Validators.required], notifyNominators: [this.program.notifyNominators] }); if (!this.isNomination) { this.formData = this.program.masterResponse ? this.program.masterResponse.masterResponse : null; await this.eligibilityFormChanged(true); this.setCustomMessageForm(true); } this.updateValidation(); this.ready = true; } updateValidation () { if (this.program.publishedValidityAlert || this.program.draftValidityAlert) { if (!this.program.defaultForm) { this.markAsRequired('defaultForm'); } } } markAsRequired (controlName: keyof ProgramFormGroup) { this.formBuilderFactory.markAsRequired(this.formGroup, controlName); } async setMasterForm (isInit = false) { if (this.eligibilityFormId && this.eligibilityRevisionId) { this.masterForm = await this.formLogicService.getAndSetForm( this.eligibilityFormId, this.eligibilityRevisionId ); this.formDefinition = this.masterForm.formDefinition; if (!isInit && this.formData) { Object.keys(this.formData) .forEach(key => delete this.formData[key]); } else if (this.formData) { // Check to see if components were deleted from form definition and still exist in formData response const componentKeys: string[] = []; this.formDefinition.forEach((tab) => { this.componentHelper.eachComponent( tab.components, (component) => { componentKeys.push(component.key); } ); }); Object.keys(this.formData).forEach((key) => { if ( !componentKeys.includes(key) ) { delete this.formData[key]; } }); } this.setParentFields(); this.setMasterResponse({ id: this.eligibilityFormId, grantProgramId: +this.activeProgramId, formRevisionId: this.eligibilityRevisionId, masterResponse: this.formData }); } } setParentFields () { this.parentFields = { formType: FormTypes.ELIGIBILITY, applicationId: null, applicationFormId: null, formId: this.eligibilityFormId, revisionId: this.eligibilityRevisionId, amountRequested: 0, designation: '', currencyRequestedAmountEquivalent: 0, currencyRequested: null, careOf: '', specialHandling: null, programId: +this.activeProgramId, referenceFields: Object.keys(this.formData || {}) .reduce((acc, formKey) => { const refField = this.formHelperService.getRefFieldFromFormKey( formKey, this.formDefinition ); if (refField) { return { ...acc, [refField.key]: this.formData[formKey] }; } return acc; }, {}), employeeInfo: null, inKindItems: [] }; } setMasterResponse (response: MasterResponse) { let masterResponse = response; if (response?.masterResponse) { masterResponse = { ...response, masterResponse: this.componentHelper.removeContentFromFormData( response.masterResponse, this.formDefinition ) }; } this.programService.setMapProperty( this.activeProgramId, 'masterResponse', masterResponse ); } async eligibilityFormChanged (isInit = false) { this.showEligibilityForm = false; this.spinnerService.startSpinner(); this.programService.setMapProperty( this.activeProgramId, 'eligibilityForm', this.eligibilityFormId ); if (this.eligibilityFormId) { this.setCustomMessageForm(); if (!isInit) { this.formData = null; } await this.setMasterForm(isInit); } else { this.formData = null; this.masterForm = null; this.setMasterResponse(null); } setTimeout(() => { this.showEligibilityForm = true; }); this.spinnerService.stopSpinner(); } setCustomMessageForm (setValues = false) { this.customMessagesForm = this.formBuilder.group({ success: [ this.program.successMessage || this.successMessage, Validators.required ], fail: [ this.program.failMessage || this.failMessage, Validators.required ] }); if (setValues) { this.successMessageChanged(); this.failMessageChanged(); } } successMessageChanged () { this.programService.setMapProperty( this.activeProgramId, 'successMessage', this.customMessagesForm.value.success ); } failMessageChanged () { this.programService.setMapProperty( this.activeProgramId, 'failMessage', this.customMessagesForm.value.fail ); } formDataChange (formData: FormData) { this.setFormData(formData); } onChange (change: FormioChangesWithCompKey) { const formData = { ...this.formData, [change.componentKey]: change.value }; this.setFormData(formData); } setFormData (formData: FormData) { if (!isEqual(formData, this.formData)) { this.formData = formData; this.setMasterResponse({ id: this.eligibilityFormId, grantProgramId: +this.activeProgramId, formRevisionId: this.eligibilityRevisionId, masterResponse: this.formData }); } } notifyNominatorsChange () { this.programService.setMapProperty( this.activeProgramId, 'notifyNominators', this.formGroup.value.notifyNominators ); } defaultFormChanged () { this.defaultFormChangedAlert = this.i18n.translate( 'PROGRAM:textDefaultFormChangedAlert', {}, 'The default form has been updated. All workflow levels are currently using the old form. Go to the Workflow tab to update.' ); this.programService.setMapProperty( this.activeProgramId, 'defaultForm', this.formGroup.value.defaultForm ); this.programService.setMapProperty( this.activeProgramId, 'defaultFormUpdated', true ); } ngOnDestroy () { this.sub.unsubscribe(); } }