import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { SequenceModalComponent } from '@core/components/sequence-modal/sequence-modal.component'; import { SpinnerService } from '@core/services/spinner.service'; import { AutomationAPI } from '@core/typings/api/automation.typing'; import { WorkflowLevelAdvancementAPI } from '@core/typings/api/workflow-level-advancement.typing'; import { WorkflowLevelAutomationAPI } from '@core/typings/api/workflow-level-automation.typing'; import { Automation } from '@core/typings/ui/automation.typing'; import { FormsService } from '@features/configure-forms/services/forms/forms.service'; import { ProgramService } from '@features/programs/program.service'; import { WorkflowService } from '@features/workflow/workflow.service'; import { ALL_SKIP_FILTER, ArrayHelpersService, DEFAULT_DOUBLE_VALUE, TableDataFactory, TopLevelFilter } from '@yourcause/common'; import { I18nService } from '@yourcause/common/i18n'; import { LogService } from '@yourcause/common/logging'; import { ConfirmationModalComponent, ModalFactory } from '@yourcause/common/modals'; import { NotifierService } from '@yourcause/common/notifier'; import { map, Observable } from 'rxjs'; import { WorkflowLevelAutomationService } from '../workflow-level-automation.service'; @Component({ selector: 'gc-view-rules-page', templateUrl: './view-rules-page.component.html', styleUrls: ['./view-rules-page.component.scss'] }) export class ViewRulesPageComponent { Status = AutomationAPI.AutomationRuleSetStatus; constructor ( private logger: LogService, private activatedRoute: ActivatedRoute, private formService: FormsService, private workflowService: WorkflowService, private spinnerService: SpinnerService, private i18n: I18nService, private workflowLevelAutomationService: WorkflowLevelAutomationService, private programService: ProgramService, private notifier: NotifierService, private modalFactory: ModalFactory, private arrayHelper: ArrayHelpersService, private router: Router ) { } options = [{ display: this.i18n.translate('CONFIG:textAllRules', {}, 'All rules'), value: DEFAULT_DOUBLE_VALUE, dependentOptions: [{ display: '', value: ALL_SKIP_FILTER }] }, { display: this.i18n.translate('GLOBAL:textForm', {}, 'Form'), value: 'formId', dependentOptions: [{ display: this.i18n.translate('CONFIG:textAllForms', {}, 'All forms'), value: ALL_SKIP_FILTER }].concat(this.arrayHelper.sort( this.formService.published.map(form => ({ display: form.name, value: '' + form.formId })), 'display')) }, { display: this.i18n.translate('common:lblProgram', {}, 'Program'), value: 'programId', dependentOptions: [{ display: this.i18n.translate('common:textAllPrograms', {}, 'All programs'), value: ALL_SKIP_FILTER }].concat(this.arrayHelper.sort( this.programService.allPublishedActivePrograms.map(program => ({ display: program.grantProgramName, value: program.grantProgramId })), 'display')) }, { display: this.i18n.translate('common:lblWorkflow', {}, 'Workflow'), value: 'workflowId', dependentOptions: [{ display: this.i18n.translate('CONFIG:textAllWorkflows', {}, 'All workflows'), value: ALL_SKIP_FILTER }].concat(this.arrayHelper.sort( this.workflowService.workflows.map(workflow => ({ display: workflow.name, value: '' + workflow.id })), 'display')) }]; tableDataFactory$: Observable> = this.activatedRoute.data.pipe( map(data => data.isAdvancement), map(isAdvancement => { return (isAdvancement ? this.workflowLevelAutomationService.advancementTableDataFactory : this.workflowLevelAutomationService.assignmentTableDataFactory) as TableDataFactory; })); drilldownRule: Automation.RuleSetForDisplay; drilldownError = false; topLevelFilters = [ new TopLevelFilter( 'doubleDropdownEquals', 'ruleProgramFormWorkflowFilter', null, null, { selectOptions: this.options }, this.i18n.translate( 'GLOBAL:textSearchRulesByFormProgramOrWorkflow', {}, 'Search rules by form, program, or workflow' ), null ), new TopLevelFilter( 'typeaheadSingleEquals', 'isDisabledOrInActive', false, null, { selectOptions: [{ value: ALL_SKIP_FILTER, display: this.i18n.translate('common:lblAllCap') }, { display: this.i18n.translate('common:textActive', {}, 'Active'), value: false }, { display: this.i18n.translate('common:textInactive', {}, 'Inactive'), value: true }] }, this.i18n.translate( 'GLOBAL:textActiveFilter', {}, 'Active filter' ) ) ]; disabledText = this.i18n.translate( 'common:textDisabled', {}, 'Disabled' ); get isAdvancement () { return this.activatedRoute.snapshot.data.isAdvancement; } async removeRule ( rule: WorkflowLevelAutomationAPI.WorkflowLevelAutomationRuleSetModel|WorkflowLevelAdvancementAPI.WorkflowLevelRoutingAutomationRuleSetModel ) { const proceed = await this.modalFactory.open( ConfirmationModalComponent, { modalHeader: this.i18n.translate( this.isAdvancement ? 'CONFIG:hdrDeleteWorkflowLevelRouting' : 'CONFIG:hdrDeleteWorkflowLevelAssignment', {}, this.isAdvancement ? 'Delete Workflow Level Routing' : 'Delete Workflow Level Assignment' ), confirmText: this.i18n.translate( this.isAdvancement ? 'CONFIG:textConfirmDeleteRouting' : 'CONFIG:textConfirmDeleteAssignment', {}, this.isAdvancement ? 'These routing rules will no longer be available after it is deleted. This action cannot be undone. Are you sure you want to continue?' : 'The assignment will no longer be available after it is deleted. This action cannot be undone. Are you sure you want to continue?' ), confirmButtonText: this.i18n.translate( 'common:btnDelete', {}, 'Delete' ) } ); if (proceed) { this.spinnerService.startSpinner(); try { const id = this.getId(rule); await this.workflowLevelAutomationService.deleteRuleSet( id, rule, this.isAdvancement ); this.notifier.success(this.i18n.translate( 'CONFIG:notificationSuccessfullyRemovedRuleS', {}, 'Successfully removed rules' )); this.notifier.success(this.i18n.translate( 'CONFIG:notificationSuccessfullyRemovedRuleS', {}, 'Successfully removed rules' )); } catch (e) { this.logger.error(e); this.notifier.error(this.i18n.translate( 'CONFIG:notificationErrorRemovingRules', {}, 'There was an error removing your rules' )); } this.spinnerService.stopSpinner(); } } async sequence ( row: WorkflowLevelAutomationAPI.WorkflowLevelAutomationRuleSetModel | WorkflowLevelAdvancementAPI.WorkflowLevelRoutingAutomationRuleSetDetailModel ) { const { workflowId, workflowName, formId, formName, workflowLevelId, workflowLevelName } = row; this.spinnerService.startSpinner(); const results = await this.workflowLevelAutomationService.fetchSequence( workflowId, formId, this.isAdvancement ? workflowLevelId : null ); this.spinnerService.stopSpinner(); const description = ` ${this.i18n.translate( this.isAdvancement ? 'CONFIG:textRoutingRulesGroupedByDesc' : 'CONFIG:textInitialAssignmentsGroupedByDesc', {}, this.isAdvancement ? 'Routing rules are grouped by workflow, starting workflow level, and default form. Below you will find all rules that share these criteria.' : 'Initial assignments are grouped by default form and workflow. Below you will find all assignments that share these criteria.' )}

${this.i18n.translate( this.isAdvancement ? 'CONFIG:textSequenceAdvancementDescription2' : 'CONFIG:textSequenceRulesDescription2', {}, this.isAdvancement ? 'The sequence below is the order each application or nomination will be evaluated to determine the rules it will use. Submissions that do not satisfy any rule can continue to be manually routed.' : 'The sequence below is the order each application or nomination will be evaluated when submitted to determine the workflow level it will be assigned. Submissions that do not satisfy any rule will be routed to the default workflow level for the program.' )}

${this.i18n.translate( 'PROGRAM:textDefaultForm', {}, 'Default form' )}: ${formName}
${this.i18n.translate( 'common:lblWorkflow', {}, 'Workflow' )}: ${workflowName}
` + ((this.workflowLevelAutomationService && this.isAdvancement) ? `
${this.i18n.translate( 'common:lblWorkflowLevel', {}, 'Workflow level' )}: ${workflowLevelName}
` : ''); const sequencedResults = await this.modalFactory.open( SequenceModalComponent, { modalHeader: this.i18n.translate( this.isAdvancement ? 'CONFIG:hdrSequenceRoutes' : 'CONFIG:hdrSequenceAssignments', {}, this.isAdvancement ? 'Sequence Routes' : 'Sequence Assignments' ), nameHeader: this.i18n.translate( this.isAdvancement ? 'CONFIG:textWorkflowLevelRoutes' : 'CONFIG:textInitialAssignment', {}, this.isAdvancement ? 'Workflow Level Routes' : 'Initial Assignment' ), description, items: results.map((result) => { return { id: this.getId(result), name: result.name, isDisabledOrInActive: result.isDisabledOrInActive, sequence: result.sequence }; }) } ); if (sequencedResults) { this.spinnerService.startSpinner(); try { await this.workflowLevelAutomationService.sequence( workflowId, formId, sequencedResults, this.isAdvancement ? workflowLevelId : null ); this.notifier.success(this.i18n.translate( 'CONFIG:notificationSuccessfullyResequenced', {}, 'Successfully resequenced rules' )); } catch (e) { this.notifier.error(this.i18n.translate( 'CONFIG:notificationFailedResequenced', {}, 'Failed to resequence rules' )); this.logger.error(e); } this.spinnerService.stopSpinner(); } } async handleDrilldown ( row: WorkflowLevelAutomationAPI.WorkflowLevelAutomationRuleSetModel|WorkflowLevelAdvancementAPI.WorkflowLevelRoutingAutomationRuleSetModel ) { this.drilldownError = false; try { const id = this.getId(row); this.drilldownRule = await this.workflowLevelAutomationService.fetchDetailForTable( id, this.isAdvancement ); } catch (e) { this.logger.error(e); this.drilldownError = true; } } async toggleStatus ( row: WorkflowLevelAutomationAPI.WorkflowLevelAutomationRuleSetModel|WorkflowLevelAdvancementAPI.WorkflowLevelRoutingAutomationRuleSetModel ) { const currentStatus = row.status; let confirmText: string; let modalHeader: string; let confirmButtonText: string; let nextStatus: AutomationAPI.AutomationRuleSetStatus; if (currentStatus === AutomationAPI.AutomationRuleSetStatus.Enabled) { nextStatus = AutomationAPI.AutomationRuleSetStatus.Disabled; confirmText = this.i18n.translate( this.isAdvancement ? 'CONFIG:textDeactivateRoutingRuleConfirm' : 'CONFIG:textDeactivateAssignmentConfirm', {}, this.isAdvancement ? `Deactivating this rule will prevent it from being used when evaluating future actions. Deactivated routing rules can be reactivated by accessing the dropdown on the Workflow Level Routing tab.` : `Deactivating this assignment will prevent it from being used when evaluating future submissions. Deactivated assignments can be reactivated by accessing the dropdown on the Workflow Level Assignments tab.` ); modalHeader = this.i18n.translate( this.isAdvancement ? 'CONFIG:hdrDeactivateRoutingRule' : 'CONFIG:hdrDeactivateAssignment', {}, this.isAdvancement ? 'Deactivate Routing Rule' : 'Deactivate Assignment' ); confirmButtonText = this.i18n.translate( 'GLOBAL:btnDeactivate', {}, 'Deactivate' ); } else { nextStatus = AutomationAPI.AutomationRuleSetStatus.Enabled; confirmText = this.i18n.translate( this.isAdvancement ? 'CONFIG:textActivateRulesConfirmTextRouting' : 'CONFIG:textActivateRulesConfirmTextAssignment', {}, this.isAdvancement ? `Activating this rule will route applications when they meet the rules' criteria. Applications already in the workflow level that have met the criteria will NOT be automatically routed.` : 'Activating this assignment will route applications when they meet the rules’ criteria.' ); modalHeader = this.i18n.translate( this.isAdvancement ? 'CONFIG:hdrActivateRoutingRule' : 'CONFIG:hdrActivateAssignment', {}, this.isAdvancement ? 'Activate Routing Rule' : 'Activate Assignment' ); confirmButtonText = this.i18n.translate( 'GLOBAL:textActivate', {}, 'Activate' ); } const proceed = await this.modalFactory.open( ConfirmationModalComponent, { confirmButtonText, modalHeader, confirmText, modalSubHeader: row.name } ); if (proceed) { const id = this.getId(row); this.spinnerService.startSpinner(); await this.workflowLevelAutomationService.changeRuleSetStatus( id, row, nextStatus, this.isAdvancement ); this.spinnerService.stopSpinner(); } } getId ( row: WorkflowLevelAutomationAPI.WorkflowLevelAutomationRuleSetModel | WorkflowLevelAdvancementAPI.WorkflowLevelRoutingAutomationRuleSetModel ) { return this.workflowLevelAutomationService.getId(row); } async copyRule ( row: WorkflowLevelAutomationAPI.WorkflowLevelAutomationRuleSetModel | WorkflowLevelAdvancementAPI.WorkflowLevelRoutingAutomationRuleSetModel ) { this.spinnerService.startSpinner(); await this.handleDrilldown(row); this.spinnerService.stopSpinner(); if (this.drilldownError) { return this.notifier.error(this.i18n.translate( 'CONFIG:textTheSelectedRuleInvalidCantBeCopied', {}, 'The selected rule is invalid and cannot be copied. Please edit the rule to fix validation before copying.' )); } const response = await this.modalFactory.open( ConfirmationModalComponent, { modalHeader: this.i18n.translate( 'CONFIG:hdrCopyRule', {}, 'Copy Rule' ), modalSubHeader: row.name, confirmButtonText: this.i18n.translate( 'common:textCopy', {}, 'Copy' ), confirmText: this.i18n.translate( 'CONFIG:textAreYouSureCopyRule', {}, 'Are you sure you want to copy the rule? A new rule will be created with the same configuration.' ) } ); if (response) { this.spinnerService.startSpinner(); let id: number; if (this.isAdvancement) { id = await this.workflowLevelAutomationService.handleCopyAdvancementRule( row as WorkflowLevelAdvancementAPI.WorkflowLevelRoutingAutomationRuleSetModel ); } else { id = await this.workflowLevelAutomationService.handleCopyInitialRule( row as WorkflowLevelAutomationAPI.WorkflowLevelAutomationRuleSetModel ); } this.spinnerService.stopSpinner(); if (id) { this.router.navigate([ '/management/program-setup/workflow-automation/' + (this.isAdvancement ? 'advancement/' + id : 'initial/' + id) ]); } } } }