import { Component, OnDestroy, OnInit } 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 { ProgramApplicantType, WorkflowFormMap } from '@core/typings/program.typing'; import { WorkflowDetail, WorkflowLevel } from '@core/typings/workflow.typing'; import { AvailabilityOptions, CompletionRequirementType, FormTypes, ResponseVisibilityOptions, WorkflowLevelFormAdd, WorkflowLevelFormApi, WorkflowLevelFormRemove } from '@features/configure-forms/form.typing'; import { FormsService } from '@features/configure-forms/services/forms/forms.service'; import { WorkflowService } from '@features/workflow/workflow.service'; import { ArrayHelpersService, TypeaheadSelectOption, TypeSafeFormBuilder, TypeSafeFormGroup } from '@yourcause/common'; import { AnalyticsService, EventType } from '@yourcause/common/analytics'; import { I18nService } from '@yourcause/common/i18n'; import { Subscription } from 'rxjs'; import { ProgramService } from '../program.service'; interface ProgramWorkflowFormGroup { workflow: number; defaultLevel: number; showDisabledWorkflowLevels: boolean; } @Component({ selector: 'gc-program-workflow', templateUrl: './program-workflow.component.html', styleUrls: ['./program-workflow.component.scss'] }) export class ProgramWorkflowComponent implements OnInit, OnDestroy { formGroup: TypeSafeFormGroup; defaultSelectWorkflow = this.i18n.translate( 'PROGRAM:textSelecttWorkflowForProgram', {}, 'Select a workflow for this program' ); defaultSelectWorkflowLevel = this.i18n.translate( 'PROGRAM:textSelectDefaultWorkflowLevelForProgram', {}, 'Select a default workflow level for this program' ); workflowOptions: TypeaheadSelectOption[] = this.arrayHelper.sort( this.workflowService.workflows.map((workflow) => { return { label: workflow.name, value: workflow.id }; }), 'label' ); levelOptions: TypeaheadSelectOption[] = []; workflowDetail: WorkflowDetail; ApplicantType = ProgramApplicantType; defaultForm: WorkflowLevelFormApi; canUpdateDefaultForm = false; hasDisabledLevels = false; sub = new Subscription(); constructor ( private formBuilder: TypeSafeFormBuilder, private programService: ProgramService, private i18n: I18nService, private spinnerService: SpinnerService, private workflowService: WorkflowService, private formService: FormsService, private arrayHelper: ArrayHelpersService, private formBuilderFactory: FormBuilderFactoryService, private analyticsService: AnalyticsService ) { 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 workflowFormMap () { return this.program.workflowFormMap || {}; } get defaultFormName () { const found = this.formService.published.find((form) => { return +form.formId === +this.program.defaultForm; }); return found ? found.name : ''; } async ngOnInit () { this.spinnerService.startSpinner(); this.formGroup = this.formBuilder.group({ workflow: [this.program.workflow, Validators.required], defaultLevel: [this.program.defaultLevel, Validators.required], showDisabledWorkflowLevels: false }); this.defaultForm = { formId: this.program.defaultForm, workflowLevelId: null, formType: !this.isNomination ? FormTypes.REQUEST : FormTypes.NOMINATION, managerActionType: ResponseVisibilityOptions.VIEW_AT_ALL_WORKFLOW, completionRequirementType: CompletionRequirementType.NONE, isDefaultForm: true, specificNumberForCompletion: null, portalAvailability: AvailabilityOptions.AUTO, portalAvailabilityDetails: null, dueDateDetails: null, clientEmailTemplateId: null, sortOrder: 1 }; if (this.formGroup.value.workflow) { await this.workflowChanged(true); } this.setCanUpdateDefaultForm(); this.updateValidation(); this.spinnerService.stopSpinner(); } async workflowChanged (isInit = false) { if (!isInit) { this.formGroup.get('defaultLevel').setValue(null); } this.spinnerService.startSpinner(); const workflowId = +this.formGroup.value.workflow; await this.programService.setWorkflowMap(workflowId); this.workflowDetail = this.workflowService.get('workflowMap')[workflowId]; this.setLevelOptions(); this.spinnerService.stopSpinner(); // If Workflow changes, reset workflow && workflow form map if (workflowId !== this.program.workflow) { this.programService.setMapProperty( this.activeProgramId, 'workflow', workflowId ); const workflowFormMap: WorkflowFormMap = {}; this.workflowDetail.levels.forEach((lev) => { workflowFormMap[lev.id] = [ { ...this.defaultForm, workflowLevelId: lev.id } ]; lev.subLevels.forEach((sub) => { workflowFormMap[sub.id] = [ { ...this.defaultForm, workflowLevelId: sub.id } ]; }); }); this.programService.setMapProperty( this.activeProgramId, 'workflowFormMap', workflowFormMap ); } this.setCanUpdateDefaultForm(); } setLevelOptions () { if (this.workflowDetail) { this.hasDisabledLevels = false; const levelOptions: TypeaheadSelectOption[] = []; this.workflowDetail.levels.forEach((level: WorkflowLevel) => { if (level.disabled) { this.hasDisabledLevels = true; } else { levelOptions.push({ label: level.name, value: level.id }); } level.subLevels.forEach((sub: WorkflowLevel) => { if (sub.disabled) { this.hasDisabledLevels = true; } else { levelOptions.push({ label: sub.name, value: sub.id }); } }); }); this.levelOptions = this.arrayHelper.sort(levelOptions, 'label'); } else { this.levelOptions = []; } } levelChanged () { const defaultLevel = this.formGroup.value.defaultLevel; this.programService.setMapProperty( this.activeProgramId, 'defaultLevel', defaultLevel ); } onFormChangeAdd (response: WorkflowLevelFormAdd) { const id = response.row.id; const current = this.workflowFormMap[id] || []; let workflowMap = { ...this.workflowFormMap, [id]: [ ...(current), response.response ] }; if (response.form) { workflowMap = { ...this.workflowFormMap, [id]: [ ...current.slice(0, response.index), response.response, ...current.slice(response.index + 1) ] }; } this.programService.setMapProperty( this.activeProgramId, 'workflowFormMap', workflowMap ); } onFormOrderChange (payload: { id: number; updatedList: WorkflowLevelFormApi[]; } ) { const workflowMap = { ...this.workflowFormMap, [payload.id]: payload.updatedList }; this.programService.setMapProperty( this.activeProgramId, 'workflowFormMap', workflowMap ); } onFormChangeRemove (response: WorkflowLevelFormRemove) { const id = response.row.id; const current = this.workflowFormMap[id] || []; this.programService.setMapProperty( this.activeProgramId, 'workflowFormMap', { ...this.workflowFormMap, [id]: [ ...current.slice(0, response.index), ...current.slice(response.index + 1) ] } ); } setCanUpdateDefaultForm () { if (this.program.defaultFormUpdated) { let canUpdateDefaultForm = false; if (this.workflowDetail && this.program.defaultForm) { this.workflowDetail.levels.forEach((level) => { if (!this.hasDefault(level.id)) { canUpdateDefaultForm = true; } level.subLevels.forEach((sub) => { if (!this.hasDefault(sub.id)) { canUpdateDefaultForm = true; } }); }); this.canUpdateDefaultForm = canUpdateDefaultForm; } this.canUpdateDefaultForm = canUpdateDefaultForm; } } hasDefault (id: number) { return this.workflowFormMap[id].some((form) => { return form.formId === this.program.defaultForm; }); } updateDefaultForm () { const map = this.program.workflowFormMap; Object.keys(map).forEach((level) => { let formExists = false; map[level] = map[level].filter((form) => { if (form.formId === this.program.defaultForm) { formExists = true; } if (form.isDefaultForm && form.formId !== this.program.defaultForm) { return false; } return true; }); if (!formExists) { map[level] = [ { ...this.defaultForm, workflowLevelId: +level }, ...map[level] ]; } }); this.programService.setMapProperty( this.activeProgramId, 'workflowFormMap', map ); this.analyticsService.emitEvent({ eventName: 'Update default form', eventType: EventType.Click, extras: null }); this.setCanUpdateDefaultForm(); } updateValidation () { if (this.program.draftValidityAlert || this.program.publishedValidityAlert) { if (!this.program.workflow) { this.markAsRequired('workflow'); } } if (this.program.publishedValidityAlert) { if (!this.program.defaultLevel) { this.markAsRequired('defaultLevel'); } } } markAsRequired (controlName: keyof ProgramWorkflowFormGroup) { this.formBuilderFactory.markAsRequired(this.formGroup, controlName); } ngOnDestroy () { this.sub.unsubscribe(); } }