[ACS-4538] Make folder-rules secondary entry point of aca-content (#3181)

* [ACS-4538] Make folder-rules secondary entry point of aca-content

* [ACA-4538] Fix folder rules imports

* [ACS-4538] Fix folder-rules unit tests

* [ACS-4538] Fix package.json after folder rules transition

* [ACS-4538] Remove duplicated peerDependency

* [ACS-4538] Import fix
This commit is contained in:
MichalKinas
2023-06-16 14:55:04 +02:00
committed by GitHub
parent 9916ecb98b
commit e0a74d7fb9
122 changed files with 111 additions and 296 deletions
@@ -0,0 +1,140 @@
<aca-page-layout>
<aca-page-layout-header>
<adf-toolbar class="adf-toolbar--inline">
<button mat-icon-button (click)="goBack()">
<mat-icon>arrow_back</mat-icon>
</button>
</adf-toolbar>
<adf-breadcrumb root="{{'ACA_FOLDER_RULES.ACTIONS.MANAGE_RULES' | translate}}"></adf-breadcrumb>
</aca-page-layout-header>
<aca-page-layout-content>
<div class="main-content">
<ng-container *ngIf="((ruleSetsLoading$ | async) && (inheritedRuleSets$ | async).length === 0) || (actionsLoading$ | async); else onLoaded">
<mat-progress-bar color="primary" mode="indeterminate"></mat-progress-bar>
</ng-container>
<ng-template #onLoaded>
<ng-container *ngIf="folderInfo$ | async; else genericError">
<adf-toolbar class="adf-toolbar--inline aca-manage-rules__actions-bar">
<adf-toolbar-title class="aca-manage-rules__actions-bar__title">
<mat-icon class="icon-aligner">folder</mat-icon>
<adf-breadcrumb root="{{ (folderInfo$ | async).name }}:{{'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.BREADCRUMB.RULES' | translate}}"
class="aca-manage-rules__actions-bar__title__breadcrumb"></adf-breadcrumb>
</adf-toolbar-title>
<mat-slide-toggle
data-automation-id="manage-rules-inheritance-toggle-button"
[checked]="isInheritanceEnabled"
(change)="onInheritanceToggleChange($event)"
[disabled]="isInheritanceToggleDisabled"
[labelPosition]="'before'">
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.INHERIT_RULES' | translate }}
</mat-slide-toggle>
<mat-divider vertical class="vertical-divider"></mat-divider>
<div class="aca-manage-rules__actions-bar__buttons">
<button
*ngIf="!(mainRuleSet$ | async)"
data-automation-id="manage-rules-link-button"
mat-stroked-button
(click)="openLinkRulesDialog()">
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.LINK_RULES' | translate }}
</button>
<button
*ngIf="canEditMainRule"
data-automation-id="manage-rules-create-button"
mat-flat-button color="primary"
(click)="openCreateUpdateRuleDialog()">
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.CREATE_RULE' | translate }}
</button>
</div>
</adf-toolbar>
<mat-divider></mat-divider>
<div class="aca-manage-rules__container" *ngIf="isMainRuleSetNotEmpty || isInheritedRuleSetsNotEmpty; else emptyContent">
<aca-rule-list
[mainRuleSet]="mainRuleSet$ | async"
[folderId]="nodeId"
[inheritedRuleSets]="inheritedRuleSets$ | async"
[hasMoreRuleSets]="hasMoreRuleSets$ | async"
[ruleSetsLoading]="ruleSetsLoading$ | async"
[selectedRule]="selectedRule$ | async"
(loadMoreRuleSets)="onLoadMoreRuleSets()"
(loadMoreRules)="onLoadMoreRules($event)"
(selectRule)="onSelectRule($event)"
(ruleEnabledChanged)="onRuleEnabledToggle($event[0], $event[1])"
(ruleSetEditLinkClicked)="openLinkRulesDialog($event)"
(ruleSetUnlinkClicked)="onRuleSetUnlinkClicked($event)">
</aca-rule-list>
<div class="aca-manage-rules__container__rule-details">
<div class="aca-manage-rules__container__rule-details__header" *ngIf="(selectedRule$ | async) as selectedRule">
<div class="aca-manage-rules__container__rule-details__header__title">
<div class="aca-manage-rules__container__rule-details__header__title__name">
{{ selectedRule.name }}
</div>
<div class="aca-manage-rules__container__rule-details__header__title__description">
{{ selectedRule.description }}
</div>
</div>
<div class="aca-manage-rules__container__rule-details__header__buttons">
<ng-container *ngIf="canEditSelectedRule; else goToFolderButton">
<button mat-stroked-button (click)="onRuleDeleteButtonClicked(selectedRule)" id="delete-rule-btn">
<mat-icon>delete_outline</mat-icon>
</button>
<button mat-stroked-button (click)="openCreateUpdateRuleDialog(selectedRule)" id="edit-rule-btn">
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.EDIT_RULE' | translate }}
</button>
</ng-container>
<ng-template #goToFolderButton>
<button mat-stroked-button [routerLink]="['/nodes', (selectedRuleSet$ | async).owningFolder.id, 'rules']">
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.SEE_IN_FOLDER' | translate }}
</button>
</ng-template>
</div>
</div>
<div class="aca-manage-rules__container__rule-details__content" *ngIf="(selectedRule$ | async) as selectedRule">
<aca-rule-details
[actionDefinitions]="actionDefinitions$ | async"
[readOnly]="true"
[preview]="true"
[value]="selectedRule"
[nodeId]="nodeId">
</aca-rule-details>
</div>
</div>
</div>
<ng-template #emptyContent>
<adf-empty-content
icon="library_books"
[title]="'ACA_FOLDER_RULES.MANAGE_RULES.EMPTY_RULES_LIST.TITLE' | translate"
[subtitle]="'ACA_FOLDER_RULES.MANAGE_RULES.EMPTY_RULES_LIST.SUBTITLE' | translate"
>
</adf-empty-content>
</ng-template>
</ng-container>
<ng-template #genericError>
<aca-page-layout-error>
<aca-generic-error></aca-generic-error>
</aca-page-layout-error>
</ng-template>
</ng-template>
</div>
</aca-page-layout-content>
</aca-page-layout>
@@ -0,0 +1,103 @@
.aca-manage-rules {
&__actions-bar {
padding: 0 30px;
display: flex;
align-items: center;
&__title {
align-items: center;
width: unset !important;
flex: 1;
&__breadcrumb {
margin-left: 18px;
}
}
&__buttons {
display: flex;
align-items: stretch;
gap: 12px;
}
.mat-slide-toggle-label {
font-weight: 600;
font-size: 14px;
color: var(--adf-breadcrumb-item-active-color);
}
.vertical-divider {
height: 50%;
margin: 0 12px;
}
}
&__container {
display: grid;
grid-template-columns: minmax(250px, 1fr) 3fr;
padding: 20px;
gap: 12px;
overflow-y: auto;
flex: 1;
&__rule-details {
border: 1px solid var(--theme-border-color);
border-radius: 12px;
overflow-y: auto;
&__header {
position: sticky;
top: 0;
z-index: 1;
display: flex;
flex-direction: row;
align-items: flex-start;
overflow-x: auto;
justify-content: space-between;
padding: 12px 20px;
border-bottom: 1px solid var(--theme-border-color);
background-color: var(--theme-background-color);
column-gap: 20px;
&__title {
display: flex;
flex-direction: column;
gap: 4px;
word-wrap: break-word;
min-width: 100px;
&__name {
font-size: 1.2em;
font-weight: bold;
}
&__description {
font-size: 0.8em;
font-style: italic;
}
}
&__buttons {
display: flex;
flex-direction: row;
align-items: stretch;
gap: 4px;
button {
color: var(--theme-text-color);
&#delete-rule-btn {
padding: 0 8px;
min-width: unset;
}
.mat-icon {
// Something pops out of this button for some reason so this is necessary
overflow: hidden !important;
}
}
}
}
}
}
}
@@ -0,0 +1,341 @@
/*!
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Alfresco Example Content Application
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AcaFolderRulesModule } from '../folder-rules.module';
import { ManageRulesSmartComponent } from './manage-rules.smart-component';
import { DebugElement, Predicate } from '@angular/core';
import { CoreTestingModule } from '@alfresco/adf-core';
import { FolderRulesService } from '../services/folder-rules.service';
import { ActivatedRoute } from '@angular/router';
import { BehaviorSubject, of, Subject } from 'rxjs';
import {
inheritedRuleSetMock,
inheritedRuleSetWithEmptyRulesMock,
inheritedRuleSetWithOnlyDisabledRulesMock,
ownedRuleSetMock,
ruleSetWithLinkMock
} from '../mock/rule-sets.mock';
import { By } from '@angular/platform-browser';
import { getOwningFolderEntryMock, owningFolderIdMock, owningFolderMock } from '../mock/node.mock';
import { MatDialog } from '@angular/material/dialog';
import { ActionsService } from '../services/actions.service';
import { FolderRuleSetsService } from '../services/folder-rule-sets.service';
import { ruleMock, ruleSettingsMock } from '../mock/rules.mock';
import { Store } from '@ngrx/store';
import { AppService } from '@alfresco/aca-shared';
describe('ManageRulesSmartComponent', () => {
let fixture: ComponentFixture<ManageRulesSmartComponent>;
let component: ManageRulesSmartComponent;
let debugElement: DebugElement;
let folderRuleSetsService: FolderRuleSetsService;
let folderRulesService: FolderRulesService;
let actionsService: ActionsService;
let callApiSpy: jasmine.Spy;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CoreTestingModule, AcaFolderRulesModule],
providers: [
{
provide: AppService,
useValue: {
appNavNarMode$: new BehaviorSubject('expanded'),
toggleAppNavBar$: new Subject()
}
},
FolderRuleSetsService,
FolderRulesService,
{ provide: Store, useValue: { dispatch: () => {} } },
{ provide: ActivatedRoute, useValue: { params: of({ nodeId: owningFolderIdMock }) } }
]
});
fixture = TestBed.createComponent(ManageRulesSmartComponent);
component = fixture.componentInstance;
debugElement = fixture.debugElement;
folderRuleSetsService = TestBed.inject(FolderRuleSetsService);
folderRulesService = TestBed.inject(FolderRulesService);
actionsService = TestBed.inject(ActionsService);
spyOn(actionsService, 'loadActionDefinitions').and.stub();
spyOn(folderRulesService, 'getRuleSettings').and.returnValue(Promise.resolve(ruleSettingsMock));
callApiSpy = spyOn<any>(folderRuleSetsService, 'callApi');
callApiSpy
.withArgs(`/nodes/${owningFolderIdMock}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=0&maxItems=100`, 'GET')
.and.returnValue(Promise.resolve(ownedRuleSetMock))
.withArgs(`/nodes/${owningFolderIdMock}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET')
.and.returnValue(Promise.resolve(ownedRuleSetMock))
.withArgs(`/nodes/${owningFolderIdMock}?include=path%2Cproperties%2CallowableOperations%2Cpermissions`, 'GET')
.and.returnValue(Promise.resolve(getOwningFolderEntryMock));
});
afterEach(() => {
fixture.destroy();
});
it('should show a list of rule sets and rules', () => {
const loadRuleSetsSpy = spyOn(folderRuleSetsService, 'loadRuleSets').and.stub();
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetMock]);
folderRuleSetsService.isLoading$ = of(false);
folderRulesService.selectedRule$ = of(ruleMock('owned-rule-1'));
actionsService.loading$ = of(false);
fixture.detectChanges();
expect(component).toBeTruthy();
expect(loadRuleSetsSpy).toHaveBeenCalledOnceWith(component.nodeId);
const ruleGroupingSections = debugElement.queryAll(By.css(`[data-automation-id="rule-list-item"]`));
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
const deleteRuleBtn = debugElement.query(By.css('#delete-rule-btn'));
expect(ruleGroupingSections.length).toBe(2, 'unexpected number of rule sections');
expect(rules.length).toBe(4, 'unexpected number of aca-rule-list-item');
expect(ruleDetails).toBeTruthy('aca-rule-details was not rendered');
expect(deleteRuleBtn).toBeTruthy('no delete rule button');
});
it('should show adf-empty-content if node has no rules defined yet', () => {
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
folderRuleSetsService.mainRuleSet$ = of(null);
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetWithEmptyRulesMock]);
folderRuleSetsService.isLoading$ = of(false);
actionsService.loading$ = of(false);
fixture.detectChanges();
expect(component).toBeTruthy();
const adfEmptyContent = debugElement.query(By.css('adf-empty-content'));
const ruleSets = debugElement.queryAll(By.css(`[data-automation-id="rule-set-list-item"]`));
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
expect(adfEmptyContent).toBeTruthy();
expect(ruleSets.length).toBe(0);
expect(ruleDetails).toBeFalsy();
});
it('should show adf-empty-content if there are only inherited disabled rules', () => {
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
folderRuleSetsService.mainRuleSet$ = of(null);
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetWithOnlyDisabledRulesMock]);
folderRuleSetsService.isLoading$ = of(false);
actionsService.loading$ = of(false);
fixture.detectChanges();
expect(component).toBeTruthy();
const adfEmptyContent = debugElement.query(By.css('adf-empty-content'));
const ruleSets = debugElement.queryAll(By.css(`[data-automation-id="rule-set-list-item"]`));
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
expect(adfEmptyContent).toBeTruthy();
expect(ruleSets.length).toBe(0);
expect(ruleDetails).toBeFalsy();
});
it('should only show aca-generic-error if the non-existing node was provided', () => {
folderRuleSetsService.folderInfo$ = of(null);
folderRuleSetsService.mainRuleSet$ = of(null);
folderRuleSetsService.inheritedRuleSets$ = of([]);
folderRuleSetsService.isLoading$ = of(false);
actionsService.loading$ = of(false);
fixture.detectChanges();
expect(component).toBeTruthy();
const acaGenericError = debugElement.query(By.css('aca-generic-error'));
const rules = debugElement.query(By.css('.aca-rule-list-item'));
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
expect(acaGenericError).toBeTruthy();
expect(rules).toBeFalsy();
expect(ruleDetails).toBeFalsy();
});
it('should only show progress bar while loading', async () => {
folderRuleSetsService.folderInfo$ = of(null);
folderRuleSetsService.mainRuleSet$ = of(null);
folderRuleSetsService.inheritedRuleSets$ = of([]);
folderRuleSetsService.isLoading$ = of(true);
actionsService.loading$ = of(true);
fixture.detectChanges();
expect(component).toBeTruthy();
const matProgressBar = debugElement.query(By.css('mat-progress-bar'));
const rules = debugElement.query(By.css('.aca-rule-list-item'));
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
expect(matProgressBar).toBeTruthy();
expect(rules).toBeFalsy();
expect(ruleDetails).toBeFalsy();
});
it('should call deleteRule() if confirmation dialog returns true', () => {
const dialog = TestBed.inject(MatDialog);
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetMock]);
folderRuleSetsService.isLoading$ = of(false);
folderRulesService.selectedRule$ = of(ruleMock('owned-rule-1'));
folderRulesService.deletedRuleId$ = of(null);
actionsService.loading$ = of(false);
const onRuleDeleteButtonClickedSpy = spyOn(component, 'onRuleDeleteButtonClicked').and.callThrough();
const dialogResult: any = {
afterClosed: () => of(true)
};
const dialogOpenSpy = spyOn(dialog, 'open').and.returnValue(dialogResult);
const deleteRuleSpy = spyOn(folderRulesService, 'deleteRule');
const onRuleDeleteSpy = spyOn(component, 'onRuleDelete').and.callThrough();
fixture.detectChanges();
expect(component).toBeTruthy('expected component');
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
const deleteRuleBtn = fixture.debugElement.nativeElement.querySelector('#delete-rule-btn');
deleteRuleBtn.click();
fixture.detectChanges();
folderRulesService.deletedRuleId$ = of('owned-rule-1-id');
expect(onRuleDeleteButtonClickedSpy).toHaveBeenCalled();
expect(dialogOpenSpy).toHaveBeenCalled();
expect(deleteRuleSpy).toHaveBeenCalled();
expect(onRuleDeleteSpy).toHaveBeenCalledTimes(1);
expect(rules).toBeTruthy('expected rules');
expect(ruleDetails).toBeTruthy('expected ruleDetails');
expect(deleteRuleBtn).toBeTruthy();
});
describe('Create rule & link rules buttons visibility', () => {
let createButtonPredicate: Predicate<DebugElement>;
let linkButtonPredicate: Predicate<DebugElement>;
beforeEach(() => {
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
folderRuleSetsService.inheritedRuleSets$ = of([]);
folderRuleSetsService.isLoading$ = of(false);
actionsService.loading$ = of(false);
createButtonPredicate = By.css(`[data-automation-id="manage-rules-create-button"]`);
linkButtonPredicate = By.css(`[data-automation-id="manage-rules-link-button"]`);
});
it('should show the create rule button if there is no main rule set', () => {
folderRuleSetsService.mainRuleSet$ = of(null);
fixture.detectChanges();
const createButton = debugElement.query(createButtonPredicate);
expect(createButton).toBeTruthy();
});
it('should show the link rules button if there is no main rule set', () => {
folderRuleSetsService.mainRuleSet$ = of(null);
fixture.detectChanges();
const linkButton = debugElement.query(linkButtonPredicate);
expect(linkButton).toBeTruthy();
});
it('should show the create rule button if the main rule set is owned', () => {
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
fixture.detectChanges();
const createButton = debugElement.query(createButtonPredicate);
expect(createButton).toBeTruthy();
});
it('should not show the create rule button if the main rule set is linked', () => {
folderRuleSetsService.mainRuleSet$ = of(ruleSetWithLinkMock);
fixture.detectChanges();
const createButton = debugElement.query(createButtonPredicate);
expect(createButton).toBeFalsy();
});
it('should not show the link rules button if the folder has a main rule set', () => {
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
fixture.detectChanges();
const linkButton = debugElement.query(linkButtonPredicate);
expect(linkButton).toBeFalsy();
});
});
describe('Rule inheritance toggle button', () => {
beforeEach(() => {
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
folderRuleSetsService.inheritedRuleSets$ = of([]);
folderRuleSetsService.isLoading$ = of(false);
actionsService.loading$ = of(false);
});
it('should show inherit rules toggle button, and disable it when isInheritanceToggleDisabled = true', () => {
fixture.detectChanges();
const createButton = debugElement.query(By.css(`[data-automation-id="manage-rules-inheritance-toggle-button"]`));
expect(createButton).toBeTruthy();
component.isInheritanceToggleDisabled = true;
fixture.detectChanges();
expect(createButton.nativeNode.classList).toContain('mat-disabled');
});
it('should call onInheritanceToggleChange() on change', () => {
const onInheritanceToggleChangeSpy = spyOn(component, 'onInheritanceToggleChange').and.callThrough();
const updateRuleSettingsSpy = spyOn(folderRulesService, 'updateRuleSettings').and.returnValue(Promise.resolve(ruleSettingsMock));
const loadRuleSetsSpy = spyOn(folderRuleSetsService, 'loadRuleSets').and.callThrough();
fixture.detectChanges();
const inheritanceToggleBtn = fixture.debugElement.query(By.css(`[data-automation-id="manage-rules-inheritance-toggle-button"]`));
inheritanceToggleBtn.nativeElement.dispatchEvent(new Event('change'));
fixture.detectChanges();
expect(onInheritanceToggleChangeSpy).toHaveBeenCalled();
expect(updateRuleSettingsSpy).toHaveBeenCalledTimes(1);
expect(loadRuleSetsSpy).toHaveBeenCalledTimes(1);
});
});
});
@@ -0,0 +1,272 @@
/*!
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Alfresco Example Content Application
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { Location } from '@angular/common';
import { FolderRulesService } from '../services/folder-rules.service';
import { Observable, Subject, Subscription } from 'rxjs';
import { Rule } from '../model/rule.model';
import { ActivatedRoute } from '@angular/router';
import { NodeInfo } from '@alfresco/aca-shared/store';
import { delay, takeUntil } from 'rxjs/operators';
import { EditRuleDialogUiComponent } from '../rule-details/edit-rule-dialog.ui-component';
import { MatDialog } from '@angular/material/dialog';
import { ConfirmDialogComponent } from '@alfresco/adf-content-services';
import { NotificationService } from '@alfresco/adf-core';
import { ActionDefinitionTransformed } from '../model/rule-action.model';
import { ActionsService } from '../services/actions.service';
import { FolderRuleSetsService } from '../services/folder-rule-sets.service';
import { RuleSet } from '../model/rule-set.model';
import { RuleSetPickerSmartComponent } from '../rule-set-picker/rule-set-picker.smart-component';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { ActionParameterConstraint } from '../model/action-parameter-constraint.model';
@Component({
selector: 'aca-manage-rules',
templateUrl: 'manage-rules.smart-component.html',
styleUrls: ['manage-rules.smart-component.scss'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.Default,
host: { class: 'aca-manage-rules' }
})
export class ManageRulesSmartComponent implements OnInit, OnDestroy {
nodeId = '';
isInheritanceEnabled = true;
isInheritanceToggleDisabled = false;
mainRuleSet$: Observable<RuleSet>;
inheritedRuleSets$: Observable<RuleSet[]>;
selectedRule$: Observable<Rule>;
selectedRuleSet$: Observable<RuleSet>;
hasMoreRuleSets$: Observable<boolean>;
ruleSetsLoading$: Observable<boolean>;
folderInfo$: Observable<NodeInfo>;
actionsLoading$: Observable<boolean>;
actionDefinitions$: Observable<ActionDefinitionTransformed[]>;
parameterConstraints$: Observable<ActionParameterConstraint[]>;
canEditMainRule = false;
canEditSelectedRule = false;
isMainRuleSetNotEmpty = false;
isInheritedRuleSetsNotEmpty = false;
private destroyed$ = new Subject<void>();
private _actionDefinitionsSub: Subscription;
constructor(
private location: Location,
private folderRulesService: FolderRulesService,
private route: ActivatedRoute,
private matDialogService: MatDialog,
private notificationService: NotificationService,
private actionsService: ActionsService,
private folderRuleSetsService: FolderRuleSetsService
) {}
ngOnInit() {
this.mainRuleSet$ = this.folderRuleSetsService.mainRuleSet$;
this.inheritedRuleSets$ = this.folderRuleSetsService.inheritedRuleSets$;
this.selectedRule$ = this.folderRulesService.selectedRule$;
this.selectedRuleSet$ = this.folderRuleSetsService.selectedRuleSet$;
this.hasMoreRuleSets$ = this.folderRuleSetsService.hasMoreRuleSets$;
this.ruleSetsLoading$ = this.folderRuleSetsService.isLoading$;
this.folderInfo$ = this.folderRuleSetsService.folderInfo$;
this.actionsLoading$ = this.actionsService.loading$.pipe(delay(0));
this.actionDefinitions$ = this.actionsService.actionDefinitionsListing$;
this.parameterConstraints$ = this.actionsService.parameterConstraints$;
this.folderRulesService.deletedRuleId$.pipe(takeUntil(this.destroyed$)).subscribe((deletedRuleId) => this.onRuleDelete(deletedRuleId));
this.actionsService.loadActionDefinitions();
this.route.params.subscribe((params) => {
this.nodeId = params.nodeId;
if (this.nodeId) {
this.folderRulesService.getRuleSettings(this.nodeId).then((ruleSettings) => {
this.isInheritanceEnabled = ruleSettings.value;
});
this.folderRuleSetsService.loadRuleSets(this.nodeId);
}
});
this._actionDefinitionsSub = this.actionDefinitions$.subscribe((actionDefinitions: ActionDefinitionTransformed[]) =>
this.actionsService.loadActionParameterConstraints(actionDefinitions)
);
this.mainRuleSet$.pipe(takeUntil(this.destroyed$)).subscribe((ruleSet) => {
this.canEditMainRule = this.canEditRule(ruleSet);
this.isMainRuleSetNotEmpty = !!ruleSet;
});
this.inheritedRuleSets$.pipe(takeUntil(this.destroyed$)).subscribe((inheritedRuleSet) => {
this.isInheritedRuleSetsNotEmpty = inheritedRuleSet.some((ruleSet) => ruleSet.rules.some((rule: Rule) => rule.isEnabled));
});
this.selectedRuleSet$.pipe(takeUntil(this.destroyed$)).subscribe((ruleSet) => {
this.canEditSelectedRule = this.canEditRule(ruleSet);
});
}
ngOnDestroy() {
this.destroyed$.next();
this.destroyed$.complete();
this._actionDefinitionsSub.unsubscribe();
}
goBack(): void {
this.location.back();
}
onSelectRule(rule: Rule) {
this.folderRulesService.selectRule(rule);
}
openCreateUpdateRuleDialog(model = {}) {
const dialogRef = this.matDialogService.open(EditRuleDialogUiComponent, {
width: '90%',
panelClass: 'aca-edit-rule-dialog-container',
data: {
model,
nodeId: this.nodeId,
parameterConstraints$: this.parameterConstraints$,
actionDefinitions$: this.actionDefinitions$
}
});
this.onSubmitRuleDialog(dialogRef);
}
onSubmitRuleDialog(dialogRef) {
dialogRef.componentInstance.submitted.subscribe(async (rule) => {
try {
if (rule.id) {
await this.onRuleUpdate(rule);
} else {
await this.onRuleCreate(rule);
}
dialogRef.close();
} catch (error) {
this.notificationService.showError(error.response.body.error.errorKey);
}
});
}
async onRuleUpdate(rule: Rule) {
const newRule = await this.folderRulesService.updateRule(this.nodeId, rule.id, rule);
this.folderRuleSetsService.addOrUpdateRuleInMainRuleSet(newRule);
}
async onRuleCreate(ruleCreateParams: Partial<Rule>) {
const newRule = await this.folderRulesService.createRule(this.nodeId, ruleCreateParams);
this.folderRuleSetsService.addOrUpdateRuleInMainRuleSet(newRule);
}
async onRuleEnabledToggle(rule: Rule, isEnabled: boolean) {
await this.folderRulesService.updateRule(this.nodeId, rule.id, { ...rule, isEnabled });
}
async onInheritanceToggleChange(event: MatSlideToggleChange) {
this.isInheritanceToggleDisabled = true;
const ruleSettings = await this.folderRulesService.updateRuleSettings(this.nodeId, '-isInheritanceEnabled-', { value: event.checked });
this.isInheritanceEnabled = ruleSettings.value;
this.folderRuleSetsService.loadRuleSets(this.nodeId);
this.isInheritanceToggleDisabled = false;
}
onRuleDeleteButtonClicked(rule: Rule) {
this.matDialogService
.open(ConfirmDialogComponent, {
data: {
title: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE.TITLE',
message: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE.MESSAGE'
},
minWidth: '346px'
})
.afterClosed()
.subscribe((result) => {
if (result) {
this.folderRulesService.deleteRule(this.nodeId, rule.id);
}
});
}
onRuleDelete(deletedRuleId: string) {
this.folderRuleSetsService.removeRuleFromMainRuleSet(deletedRuleId);
}
onLoadMoreRuleSets() {
this.folderRuleSetsService.loadMoreInheritedRuleSets();
}
onLoadMoreRules(ruleSet: RuleSet) {
this.folderRulesService.loadRules(ruleSet);
}
canEditRule(ruleSet: RuleSet): boolean {
return !ruleSet || FolderRuleSetsService.isOwnedRuleSet(ruleSet, this.nodeId);
}
openLinkRulesDialog(existingRuleSet?: RuleSet) {
this.matDialogService
.open(RuleSetPickerSmartComponent, {
width: '90%',
panelClass: 'aca-rule-set-picker-container',
data: {
nodeId: this.nodeId,
defaultNodeId: this.nodeId,
existingRuleSet
}
})
.afterClosed()
.subscribe((result) => {
if (result) {
this.folderRuleSetsService.refreshMainRuleSet();
}
});
}
onRuleSetUnlinkClicked(linkedRuleSet: RuleSet) {
this.matDialogService
.open(ConfirmDialogComponent, {
data: {
title: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE_SET_LINK.TITLE',
message: 'ACA_FOLDER_RULES.CONFIRMATION_DIALOG.DELETE_RULE_SET_LINK.MESSAGE'
},
minWidth: '346px'
})
.afterClosed()
.subscribe(async (result) => {
if (result) {
try {
await this.folderRuleSetsService.deleteRuleSetLink(this.nodeId, linkedRuleSet.id);
this.folderRuleSetsService.refreshMainRuleSet();
} catch (error) {
this.notificationService.showError('ACA_FOLDER_RULES.ERRORS.DELETE_RULE_SET_LINK_FAILED');
}
}
});
}
}