import { Component, Input, OnInit } from '@angular/core'; import { SpinnerService } from '@core/services/spinner.service'; import { Automation } from '@core/typings/ui/automation.typing'; import { ViewFormResolver } from '@features/configure-forms/resolvers/view-form.resolver'; import { RuleAutomationService } from '@features/rule-automation/rule-automation.service'; import { Tab } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { YCModalComponent } from '@yourcause/common/modals'; import { cloneDeep } from 'lodash'; import { AutomationModalFooterButtonType } from '../../pipes/program-automation-modal-footer-button.pipe'; import { ProgramAutomationService } from '../../program-automation.service'; import { AutomationBuilderModalTabType, ProgramAutomationDetailForUi, ProgramAutomationRulesetForUi } from '../../program-automation.typing'; import { DetailsFormGroup } from '../program-automation-builder-details/program-automation-builder-details.component'; import { FallbackAttrsChange } from '../program-automation-builder-fallback/program-automation-builder-fallback.component'; @Component({ selector: 'gc-program-automation-builder-modal', templateUrl: './program-automation-builder-modal.component.html', styleUrls: ['./program-automation-builder-modal.component.scss'] }) export class ProgramAutomationBuilderModalComponent extends YCModalComponent implements OnInit { @Input() id: number; detail: ProgramAutomationDetailForUi; rulesets: ProgramAutomationRulesetForUi[]; tabs: Tab[] = []; AutomationBuilderModalTabType = AutomationBuilderModalTabType; activeTabType = AutomationBuilderModalTabType.Details; activeTabIndex = 0; allTabsValid: boolean; tabValidityMap: Record = { [AutomationBuilderModalTabType.Details]: true, [AutomationBuilderModalTabType.Rules]: true, [AutomationBuilderModalTabType.Fallback_Program]: true, [AutomationBuilderModalTabType.Share]: true }; modalOpen = false; ready = false; hasStandardChanges = false; initialDraftSetting: boolean; showChangesSaved = false; hasRuleChanges = false; activeRuleset: ProgramAutomationRulesetForUi; uiRules: Automation.CriteriaFormState[]; applyCurrentRulesWithOr = false; AutomationModalFooterButtonType = AutomationModalFooterButtonType; needsUpdate = false; constructor ( private i18n: I18nService, private spinnerService: SpinnerService, private programAutomationService: ProgramAutomationService, private viewFormResolver: ViewFormResolver, private ruleAutomationService: RuleAutomationService ) { super(); } get detailsValid () { return this.tabValidityMap[AutomationBuilderModalTabType.Details]; } get rulesValid () { return this.tabValidityMap[AutomationBuilderModalTabType.Rules]; } get fallbackValid () { return this.tabValidityMap[AutomationBuilderModalTabType.Fallback_Program]; } get onRulesTab () { return this.activeTabType === AutomationBuilderModalTabType.Rules; } get onDetailsTab () { return this.activeTabType === AutomationBuilderModalTabType.Details; } get onProgramTab () { return this.activeTabType === AutomationBuilderModalTabType.Fallback_Program; } get onShareTab () { return this.activeTabType === AutomationBuilderModalTabType.Share; } async ngOnInit () { this.spinnerService.startSpinner(); const [ detail ] = await Promise.all([ this.programAutomationService.getProgramAutomationDetail(this.id), this.viewFormResolver.resolve() ]); this.detail = detail; this.rulesets = cloneDeep(detail.grantProgramRoutingAutomationRuleSets); this.initialDraftSetting = detail.draft; this.setTabs(); this.spinnerService.stopSpinner(); this.ready = true; } setTabs () { this.tabs = [{ label: this.i18n.translate( 'GLOBAL:textDetails', {}, 'Details' ), context: AutomationBuilderModalTabType.Details, active: true }, { label: this.i18n.translate( 'CONFIG:hdrRoutingRules', {}, 'Routing Rules' ), context: AutomationBuilderModalTabType.Rules, active: false }, { label: this.i18n.translate( 'common:hdrFallbackProgram', {}, 'Fallback Program' ), context: AutomationBuilderModalTabType.Fallback_Program, active: false }, { label: this.i18n.translate( 'common:hdrPublishAndShare', {}, 'Publish and Share' ), context: AutomationBuilderModalTabType.Share, active: false }]; } activeTabChanged (index: number, shouldPublish = false) { this.activeTabIndex = index; this.activeTabType = this.tabs[index].context; this.tabs.forEach((tab) => { tab.active = tab.context === this.activeTabType; }); this.saveCurrentChanges(shouldPublish); } async finish () { await this.saveCurrentChanges(); this.closeModal.emit(this.needsUpdate); } tabValidityChange (isValid: boolean, tabType: AutomationBuilderModalTabType) { this.tabValidityMap[tabType] = isValid; this.allTabsValid = Object.keys(this.tabValidityMap).every((key) => { const type = +key as AutomationBuilderModalTabType; const tabIsVisible = this.tabs.some((tab) => { return tab.context === type; }); if (tabIsVisible) { return this.tabValidityMap[type]; } return true; }); if ( this.initialDraftSetting && this.detail.complete !== this.allTabsValid ) { // If this automation started out in draft, we keep track of this this.detail.complete = this.allTabsValid; this.hasStandardChanges = true; } this.updateDisabledTabs(); } updateDisabledTabs () { this.tabs.forEach((tab) => { switch (tab.context) { case AutomationBuilderModalTabType.Details: tab.disabled = this.onRulesTab && !this.rulesValid; break; case AutomationBuilderModalTabType.Rules: tab.disabled = !this.detailsValid; break; case AutomationBuilderModalTabType.Fallback_Program: tab.disabled = !this.detailsValid || !this.rulesValid; break; case AutomationBuilderModalTabType.Share: tab.disabled = !this.detailsValid || !this.rulesValid || !this.fallbackValid || this.detail.draft; break; } }); } async saveAndClose () { await this.saveCurrentChanges(); this.closeModal.emit(this.needsUpdate); } newRoute () { this.needsUpdate = true; this.programAutomationService.setNewRouteClicked(true); } evaluationOrder () { this.programAutomationService.setEvaluationOrderClicked(true); } async saveCurrentChanges (shouldPublish = false) { if (this.hasStandardChanges || shouldPublish || this.hasRuleChanges) { this.needsUpdate = true; this.spinnerService.startSpinner(); if (this.hasStandardChanges) { const result = await this.programAutomationService.saveProgramAutomation(this.detail); if (result) { this.detail = { ...this.detail, id: result.grantProgramRoutingDetailsId, landingLinkGuid: result.grantProgramRoutingLinkGuid }; } } if (this.hasRuleChanges) { await this.saveRulesetChanges(); } if (shouldPublish) { const result = await this.programAutomationService.publishAutomationFromModal(this.detail.id); if (result.passed) { this.detail.draft = false; } } this.toggleChangesSaved(); this.spinnerService.stopSpinner(); } } onActiveRulesetChange (ruleset: ProgramAutomationRulesetForUi) { this.activeRuleset = ruleset; } async saveRulesetChanges () { if (this.hasRuleChanges) { const ruleset = await this.programAutomationService.handleSaveRuleset( this.detail.id, this.detail.formId, this.activeRuleset.grantProgramRoutingAutomationRuleSetId, this.activeRuleset.routeToGrantProgramId, this.activeRuleset.name, this.applyCurrentRulesWithOr, this.ruleAutomationService.getAdaptedRulesForAPI(this.uiRules) ); if (ruleset) { this.hasRuleChanges = false; this.onRulesetUpdated(ruleset); } } } toggleChangesSaved () { this.showChangesSaved = true; this.hasStandardChanges = false; setTimeout(() => { this.showChangesSaved = false; }, 3000); } detailAttrsChanged (changes: DetailsFormGroup) { this.hasStandardChanges = true; this.detail = { ...this.detail, name: changes.name, description: changes.description, formId: changes.formId, programApplicantType: changes.programApplicantType }; } fallbackAttrsChanged (changes: FallbackAttrsChange) { this.hasStandardChanges = true; this.detail = { ...this.detail, ruleSetFailureMessage: changes.fallbackMessage, fallbackGrantProgramId: changes.fallbackProgramId }; } onRulesetUpdated (ruleset: ProgramAutomationRulesetForUi) { const index = this.detail.grantProgramRoutingAutomationRuleSets.findIndex((set) => { return set.grantProgramRoutingAutomationRuleSetId === ruleset.grantProgramRoutingAutomationRuleSetId; }); if (index > -1) { this.detail = { ...this.detail, grantProgramRoutingAutomationRuleSets: [ ...this.detail.grantProgramRoutingAutomationRuleSets.slice(0, index), ruleset, ...this.detail.grantProgramRoutingAutomationRuleSets.slice(index + 1) ] }; } else { // Create this.detail = { ...this.detail, grantProgramRoutingAutomationRuleSets: [ ...this.detail.grantProgramRoutingAutomationRuleSets, ruleset ] }; } } onRulesetDeleted (rulesetId: number) { this.needsUpdate = true; const index = this.detail.grantProgramRoutingAutomationRuleSets.findIndex((set) => { return set.grantProgramRoutingAutomationRuleSetId === rulesetId; }); if (index > -1) { this.detail = { ...this.detail, grantProgramRoutingAutomationRuleSets: [ ...this.detail.grantProgramRoutingAutomationRuleSets.slice(0, index), ...this.detail.grantProgramRoutingAutomationRuleSets.slice(index + 1) ] }; } } }