mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2025-05-12 17:04:46 +00:00
[ACS-4010] Rule sets listing regrouping (#2803)
* [ACS-4010] Rule sets listing regrouping * Linting * Unit tests * Remove TODOs
This commit is contained in:
parent
ae551f03fc
commit
03ed8e071a
@ -92,7 +92,8 @@
|
|||||||
},
|
},
|
||||||
"ACTIONS": {
|
"ACTIONS": {
|
||||||
"CREATE_RULE": "Create rule",
|
"CREATE_RULE": "Create rule",
|
||||||
"EDIT_RULE": "Edit"
|
"EDIT_RULE": "Edit",
|
||||||
|
"SEE_IN_FOLDER": "See in folder"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"EMPTY_RULES_LIST": {
|
"EMPTY_RULES_LIST": {
|
||||||
@ -107,13 +108,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"RULE_LIST": {
|
"RULE_LIST": {
|
||||||
"OWNED_BY_THIS_FOLDER": "Owned by this folder",
|
"OWNED_RULES": "Rules from current folder",
|
||||||
"LINKED_FROM": "Linked from",
|
"LINKED_RULES": "Rules from linked folder",
|
||||||
"INHERITED_FROM": "Inherited from",
|
"INHERITED_RULES": "Inherited rules",
|
||||||
"LOAD_MORE_RULE_SETS": "Load more rule sets",
|
"LOAD_MORE_RULE_SETS": "Load rules from other folders",
|
||||||
"LOADING_RULE_SETS": "Loading rule sets",
|
|
||||||
"LOAD_MORE_RULES": "Load more rules",
|
"LOAD_MORE_RULES": "Load more rules",
|
||||||
"LOADING_RULES": "Loading rules"
|
"LOADING_RULES": "Loading rules",
|
||||||
|
"INHERITED_RULES_WILL_BE_RUN_FIRST": "Inherited rules will be run first"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,12 @@ import { RuleSimpleConditionUiComponent } from './rule-details/conditions/rule-s
|
|||||||
import { GenericErrorModule, PageLayoutModule } from '@alfresco/aca-shared';
|
import { GenericErrorModule, PageLayoutModule } from '@alfresco/aca-shared';
|
||||||
import { BreadcrumbModule, DocumentListModule } from '@alfresco/adf-content-services';
|
import { BreadcrumbModule, DocumentListModule } from '@alfresco/adf-content-services';
|
||||||
import { RuleListItemUiComponent } from './rule-list/rule-list-item/rule-list-item.ui-component';
|
import { RuleListItemUiComponent } from './rule-list/rule-list-item/rule-list-item.ui-component';
|
||||||
import { RuleListUiComponent } from './rule-list/rule-list/rule-list.ui-component';
|
import { RuleListGroupingUiComponent } from './rule-list/rule-list-grouping/rule-list-grouping.ui-component';
|
||||||
import { RuleTriggersUiComponent } from './rule-details/triggers/rule-triggers.ui-component';
|
import { RuleTriggersUiComponent } from './rule-details/triggers/rule-triggers.ui-component';
|
||||||
import { RuleOptionsUiComponent } from './rule-details/options/rule-options.ui-component';
|
import { RuleOptionsUiComponent } from './rule-details/options/rule-options.ui-component';
|
||||||
import { RuleActionListUiComponent } from './rule-details/actions/rule-action-list.ui-component';
|
import { RuleActionListUiComponent } from './rule-details/actions/rule-action-list.ui-component';
|
||||||
import { RuleActionUiComponent } from './rule-details/actions/rule-action.ui-component';
|
import { RuleActionUiComponent } from './rule-details/actions/rule-action.ui-component';
|
||||||
import { RuleSetListUiComponent } from './rule-list/rule-set-list/rule-set-list.ui-component';
|
import { RuleListUiComponent } from './rule-list/rule-list/rule-list.ui-component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -70,9 +70,9 @@ const routes: Routes = [
|
|||||||
RuleActionUiComponent,
|
RuleActionUiComponent,
|
||||||
RuleCompositeConditionUiComponent,
|
RuleCompositeConditionUiComponent,
|
||||||
RuleDetailsUiComponent,
|
RuleDetailsUiComponent,
|
||||||
RuleListUiComponent,
|
RuleListGroupingUiComponent,
|
||||||
RuleListItemUiComponent,
|
RuleListItemUiComponent,
|
||||||
RuleSetListUiComponent,
|
RuleListUiComponent,
|
||||||
RuleSimpleConditionUiComponent,
|
RuleSimpleConditionUiComponent,
|
||||||
RuleTriggersUiComponent,
|
RuleTriggersUiComponent,
|
||||||
RuleOptionsUiComponent
|
RuleOptionsUiComponent
|
||||||
|
@ -12,36 +12,44 @@
|
|||||||
<aca-page-layout-content>
|
<aca-page-layout-content>
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
|
|
||||||
<ng-container *ngIf="(ruleSetsLoading$ | async) || (actionsLoading$ | async); else onLoaded">
|
<ng-container *ngIf="((ruleSetsLoading$ | async) && (inheritedRuleSets$ | async).length === 0) || (actionsLoading$ | async); else onLoaded">
|
||||||
<mat-progress-bar color="primary" mode="indeterminate"></mat-progress-bar>
|
<mat-progress-bar color="primary" mode="indeterminate"></mat-progress-bar>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-template #onLoaded>
|
<ng-template #onLoaded>
|
||||||
<ng-container *ngIf="folderInfo$ | async; else genericError">
|
<ng-container *ngIf="folderInfo$ | async; else genericError">
|
||||||
<adf-toolbar class="adf-toolbar--inline aca-manage-rules__actions-bar">
|
<adf-toolbar class="adf-toolbar--inline aca-manage-rules__actions-bar">
|
||||||
|
|
||||||
<adf-toolbar-title class="aca-manage-rules__actions-bar__title">
|
<adf-toolbar-title class="aca-manage-rules__actions-bar__title">
|
||||||
<mat-icon class="icon-aligner">folder</mat-icon>
|
<mat-icon class="icon-aligner">folder</mat-icon>
|
||||||
<adf-breadcrumb root="{{ (folderInfo$ | async).name }}:{{'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.BREADCRUMB.RULES' | translate}}"
|
<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>
|
class="aca-manage-rules__actions-bar__title__breadcrumb"></adf-breadcrumb>
|
||||||
</adf-toolbar-title>
|
</adf-toolbar-title>
|
||||||
|
|
||||||
<button mat-flat-button color="primary" (click)="openCreateUpdateRuleDialog()">{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.CREATE_RULE' | translate }}</button>
|
<button
|
||||||
|
*ngIf="canEditRule(mainRuleSet$ | async)"
|
||||||
|
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>
|
||||||
|
|
||||||
</adf-toolbar>
|
</adf-toolbar>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
<div class="aca-manage-rules__container" *ngIf="(ruleSetListing$ | async).length > 0; else emptyContent">
|
<div class="aca-manage-rules__container" *ngIf="(mainRuleSet$ | async) || (inheritedRuleSets$ | async).length > 0; else emptyContent">
|
||||||
<aca-rule-set-list
|
<aca-rule-list
|
||||||
|
[mainRuleSet]="mainRuleSet$ | async"
|
||||||
[folderId]="nodeId"
|
[folderId]="nodeId"
|
||||||
[ruleSets]="ruleSetListing$ | async"
|
[inheritedRuleSets]="inheritedRuleSets$ | async"
|
||||||
[hasMoreRuleSets]="hasMoreRuleSets$ | async"
|
[hasMoreRuleSets]="hasMoreRuleSets$ | async"
|
||||||
[ruleSetsLoading]="ruleSetsLoading$ | async"
|
[ruleSetsLoading]="ruleSetsLoading$ | async"
|
||||||
[selectedRule]="selectedRule$ | async"
|
[selectedRule]="selectedRule$ | async"
|
||||||
(loadMoreRuleSets)="onLoadMoreRuleSets()"
|
(loadMoreRuleSets)="onLoadMoreRuleSets()"
|
||||||
(loadMoreRules)="onLoadMoreRules($event)"
|
(loadMoreRules)="onLoadMoreRules($event)"
|
||||||
(navigateToOtherFolder)="onNavigateToOtherFolder($event)"
|
|
||||||
(selectRule)="onSelectRule($event)"
|
(selectRule)="onSelectRule($event)"
|
||||||
(ruleEnabledChanged)="onRuleEnabledToggle($event[0], $event[1])">
|
(ruleEnabledChanged)="onRuleEnabledToggle($event[0], $event[1])">
|
||||||
</aca-rule-set-list>
|
</aca-rule-list>
|
||||||
|
|
||||||
<div class="aca-manage-rules__container__rule-details">
|
<div class="aca-manage-rules__container__rule-details">
|
||||||
|
|
||||||
@ -56,12 +64,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="aca-manage-rules__container__rule-details__header__buttons">
|
<div class="aca-manage-rules__container__rule-details__header__buttons">
|
||||||
<button mat-stroked-button (click)="onRuleDeleteButtonClicked(selectedRule)" id="delete-rule-btn">
|
<ng-container *ngIf="canEditRule(selectedRuleSet$ | async); else goToFolderButton">
|
||||||
<mat-icon>delete_outline</mat-icon>
|
<button mat-stroked-button (click)="onRuleDeleteButtonClicked(selectedRule)" id="delete-rule-btn">
|
||||||
</button>
|
<mat-icon>delete_outline</mat-icon>
|
||||||
<button mat-stroked-button (click)="openCreateUpdateRuleDialog(selectedRule)" id="edit-rule-btn">
|
</button>
|
||||||
{{ 'ACA_FOLDER_RULES.MANAGE_RULES.TOOLBAR.ACTIONS.EDIT_RULE' | translate }}
|
<button mat-stroked-button (click)="openCreateUpdateRuleDialog(selectedRule)" id="edit-rule-btn">
|
||||||
</button>
|
{{ '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>
|
</div>
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ import { CoreTestingModule } from '@alfresco/adf-core';
|
|||||||
import { FolderRulesService } from '../services/folder-rules.service';
|
import { FolderRulesService } from '../services/folder-rules.service';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { ruleSetsMock } from '../mock/rule-sets.mock';
|
import { inheritedRuleSetMock, ownedRuleSetMock, ruleSetWithLinkMock } from '../mock/rule-sets.mock';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { owningFolderIdMock, owningFolderMock } from '../mock/node.mock';
|
import { owningFolderIdMock, owningFolderMock } from '../mock/node.mock';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
@ -74,7 +74,8 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
const loadRuleSetsSpy = spyOn(folderRuleSetsService, 'loadRuleSets').and.stub();
|
const loadRuleSetsSpy = spyOn(folderRuleSetsService, 'loadRuleSets').and.stub();
|
||||||
|
|
||||||
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||||
folderRuleSetsService.ruleSetListing$ = of(ruleSetsMock);
|
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
|
||||||
|
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetMock]);
|
||||||
folderRuleSetsService.isLoading$ = of(false);
|
folderRuleSetsService.isLoading$ = of(false);
|
||||||
folderRulesService.selectedRule$ = of(ruleMock('owned-rule-1'));
|
folderRulesService.selectedRule$ = of(ruleMock('owned-rule-1'));
|
||||||
actionsService.loading$ = of(false);
|
actionsService.loading$ = of(false);
|
||||||
@ -85,20 +86,21 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
|
|
||||||
expect(loadRuleSetsSpy).toHaveBeenCalledOnceWith(component.nodeId);
|
expect(loadRuleSetsSpy).toHaveBeenCalledOnceWith(component.nodeId);
|
||||||
|
|
||||||
const ruleSets = debugElement.queryAll(By.css(`[data-automation-id="rule-set-list-item"]`));
|
const ruleGroupingSections = debugElement.queryAll(By.css(`[data-automation-id="rule-list-item"]`));
|
||||||
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
|
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
|
||||||
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
|
const ruleDetails = debugElement.query(By.css('aca-rule-details'));
|
||||||
const deleteRuleBtn = debugElement.query(By.css('#delete-rule-btn'));
|
const deleteRuleBtn = debugElement.query(By.css('#delete-rule-btn'));
|
||||||
|
|
||||||
expect(ruleSets.length).toBe(3, 'unexpected number of rule sets');
|
expect(ruleGroupingSections.length).toBe(2, 'unexpected number of rule sections');
|
||||||
expect(rules.length).toBe(6, 'unexpected number of aca-rule-list-item');
|
expect(rules.length).toBe(4, 'unexpected number of aca-rule-list-item');
|
||||||
expect(ruleDetails).toBeTruthy('aca-rule-details was not rendered');
|
expect(ruleDetails).toBeTruthy('aca-rule-details was not rendered');
|
||||||
expect(deleteRuleBtn).toBeTruthy('no delete rule button');
|
expect(deleteRuleBtn).toBeTruthy('no delete rule button');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only show adf-empty-content if node has no rules defined yet', () => {
|
it('should only show adf-empty-content if node has no rules defined yet', () => {
|
||||||
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||||
folderRuleSetsService.ruleSetListing$ = of([]);
|
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||||
|
folderRuleSetsService.inheritedRuleSets$ = of([]);
|
||||||
folderRuleSetsService.isLoading$ = of(false);
|
folderRuleSetsService.isLoading$ = of(false);
|
||||||
actionsService.loading$ = of(false);
|
actionsService.loading$ = of(false);
|
||||||
|
|
||||||
@ -117,7 +119,8 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
|
|
||||||
it('should only show aca-generic-error if the non-existing node was provided', () => {
|
it('should only show aca-generic-error if the non-existing node was provided', () => {
|
||||||
folderRuleSetsService.folderInfo$ = of(null);
|
folderRuleSetsService.folderInfo$ = of(null);
|
||||||
folderRuleSetsService.ruleSetListing$ = of([]);
|
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||||
|
folderRuleSetsService.inheritedRuleSets$ = of([]);
|
||||||
folderRuleSetsService.isLoading$ = of(false);
|
folderRuleSetsService.isLoading$ = of(false);
|
||||||
actionsService.loading$ = of(false);
|
actionsService.loading$ = of(false);
|
||||||
|
|
||||||
@ -136,7 +139,8 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
|
|
||||||
it('should only show progress bar while loading', async () => {
|
it('should only show progress bar while loading', async () => {
|
||||||
folderRuleSetsService.folderInfo$ = of(null);
|
folderRuleSetsService.folderInfo$ = of(null);
|
||||||
folderRuleSetsService.ruleSetListing$ = of([]);
|
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||||
|
folderRuleSetsService.inheritedRuleSets$ = of([]);
|
||||||
folderRuleSetsService.isLoading$ = of(true);
|
folderRuleSetsService.isLoading$ = of(true);
|
||||||
actionsService.loading$ = of(true);
|
actionsService.loading$ = of(true);
|
||||||
|
|
||||||
@ -156,7 +160,8 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
it('should call deleteRule() if confirmation dialog returns true', () => {
|
it('should call deleteRule() if confirmation dialog returns true', () => {
|
||||||
const dialog = TestBed.inject(MatDialog);
|
const dialog = TestBed.inject(MatDialog);
|
||||||
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||||
folderRuleSetsService.ruleSetListing$ = of(ruleSetsMock);
|
folderRuleSetsService.mainRuleSet$ = of(ownedRuleSetMock);
|
||||||
|
folderRuleSetsService.inheritedRuleSets$ = of([inheritedRuleSetMock]);
|
||||||
folderRuleSetsService.isLoading$ = of(false);
|
folderRuleSetsService.isLoading$ = of(false);
|
||||||
folderRulesService.selectedRule$ = of(ruleMock('owned-rule-1'));
|
folderRulesService.selectedRule$ = of(ruleMock('owned-rule-1'));
|
||||||
folderRulesService.deletedRuleId$ = of(null);
|
folderRulesService.deletedRuleId$ = of(null);
|
||||||
@ -191,4 +196,37 @@ describe('ManageRulesSmartComponent', () => {
|
|||||||
expect(ruleDetails).toBeTruthy('expected ruleDetails');
|
expect(ruleDetails).toBeTruthy('expected ruleDetails');
|
||||||
expect(deleteRuleBtn).toBeTruthy();
|
expect(deleteRuleBtn).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Create rule button visibility', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
folderRuleSetsService.folderInfo$ = of(owningFolderMock);
|
||||||
|
folderRuleSetsService.inheritedRuleSets$ = of([]);
|
||||||
|
folderRuleSetsService.isLoading$ = of(false);
|
||||||
|
actionsService.loading$ = of(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show the create rule button if there is no main rule set', () => {
|
||||||
|
folderRuleSetsService.mainRuleSet$ = of(null);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const createButton = debugElement.query(By.css(`[data-automation-id="manage-rules-create-button"]`));
|
||||||
|
expect(createButton).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(By.css(`[data-automation-id="manage-rules-create-button"]`));
|
||||||
|
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(By.css(`[data-automation-id="manage-rules-create-button"]`));
|
||||||
|
expect(createButton).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -29,7 +29,7 @@ import { FolderRulesService } from '../services/folder-rules.service';
|
|||||||
import { Observable, Subject } from 'rxjs';
|
import { Observable, Subject } from 'rxjs';
|
||||||
import { Rule } from '../model/rule.model';
|
import { Rule } from '../model/rule.model';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { AppStore, NavigateRouteAction, NodeInfo } from '@alfresco/aca-shared/store';
|
import { NodeInfo } from '@alfresco/aca-shared/store';
|
||||||
import { delay, takeUntil } from 'rxjs/operators';
|
import { delay, takeUntil } from 'rxjs/operators';
|
||||||
import { EditRuleDialogSmartComponent } from '../rule-details/edit-rule-dialog.smart-component';
|
import { EditRuleDialogSmartComponent } from '../rule-details/edit-rule-dialog.smart-component';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
@ -38,7 +38,6 @@ import { NotificationService } from '@alfresco/adf-core';
|
|||||||
import { ActionDefinitionTransformed } from '../model/rule-action.model';
|
import { ActionDefinitionTransformed } from '../model/rule-action.model';
|
||||||
import { ActionsService } from '../services/actions.service';
|
import { ActionsService } from '../services/actions.service';
|
||||||
import { FolderRuleSetsService } from '../services/folder-rule-sets.service';
|
import { FolderRuleSetsService } from '../services/folder-rule-sets.service';
|
||||||
import { Store } from '@ngrx/store';
|
|
||||||
import { RuleSet } from '../model/rule-set.model';
|
import { RuleSet } from '../model/rule-set.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -51,8 +50,10 @@ import { RuleSet } from '../model/rule-set.model';
|
|||||||
export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
||||||
nodeId: string = null;
|
nodeId: string = null;
|
||||||
|
|
||||||
ruleSetListing$: Observable<RuleSet[]>;
|
mainRuleSet$: Observable<RuleSet>;
|
||||||
|
inheritedRuleSets$: Observable<RuleSet[]>;
|
||||||
selectedRule$: Observable<Rule>;
|
selectedRule$: Observable<Rule>;
|
||||||
|
selectedRuleSet$: Observable<RuleSet>;
|
||||||
hasMoreRuleSets$: Observable<boolean>;
|
hasMoreRuleSets$: Observable<boolean>;
|
||||||
ruleSetsLoading$: Observable<boolean>;
|
ruleSetsLoading$: Observable<boolean>;
|
||||||
folderInfo$: Observable<NodeInfo>;
|
folderInfo$: Observable<NodeInfo>;
|
||||||
@ -69,13 +70,14 @@ export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
|||||||
private matDialogService: MatDialog,
|
private matDialogService: MatDialog,
|
||||||
private notificationService: NotificationService,
|
private notificationService: NotificationService,
|
||||||
private actionsService: ActionsService,
|
private actionsService: ActionsService,
|
||||||
private folderRuleSetsService: FolderRuleSetsService,
|
private folderRuleSetsService: FolderRuleSetsService
|
||||||
private store: Store<AppStore>
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.ruleSetListing$ = this.folderRuleSetsService.ruleSetListing$;
|
this.mainRuleSet$ = this.folderRuleSetsService.mainRuleSet$;
|
||||||
|
this.inheritedRuleSets$ = this.folderRuleSetsService.inheritedRuleSets$;
|
||||||
this.selectedRule$ = this.folderRulesService.selectedRule$;
|
this.selectedRule$ = this.folderRulesService.selectedRule$;
|
||||||
|
this.selectedRuleSet$ = this.folderRuleSetsService.selectedRuleSet$;
|
||||||
this.hasMoreRuleSets$ = this.folderRuleSetsService.hasMoreRuleSets$;
|
this.hasMoreRuleSets$ = this.folderRuleSetsService.hasMoreRuleSets$;
|
||||||
this.ruleSetsLoading$ = this.folderRuleSetsService.isLoading$;
|
this.ruleSetsLoading$ = this.folderRuleSetsService.isLoading$;
|
||||||
this.folderInfo$ = this.folderRuleSetsService.folderInfo$;
|
this.folderInfo$ = this.folderRuleSetsService.folderInfo$;
|
||||||
@ -136,24 +138,17 @@ export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onRuleUpdate(rule: Rule) {
|
async onRuleUpdate(rule: Rule) {
|
||||||
const ruleSet = this.folderRuleSetsService.getRuleSetFromRuleId(rule.id);
|
const newRule = await this.folderRulesService.updateRule(this.nodeId, rule.id, rule);
|
||||||
await this.folderRulesService.updateRule(this.nodeId, rule.id, rule, ruleSet.id);
|
this.folderRuleSetsService.addOrUpdateRuleInMainRuleSet(newRule);
|
||||||
this.folderRulesService.loadRules(ruleSet, 0, rule);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onRuleCreate(ruleCreateParams: Partial<Rule>) {
|
async onRuleCreate(ruleCreateParams: Partial<Rule>) {
|
||||||
await this.folderRulesService.createRule(this.nodeId, ruleCreateParams, '-default-');
|
const newRule = await this.folderRulesService.createRule(this.nodeId, ruleCreateParams);
|
||||||
const ruleSetToLoad = this.folderRuleSetsService.getOwnedOrLinkedRuleSet();
|
this.folderRuleSetsService.addOrUpdateRuleInMainRuleSet(newRule);
|
||||||
if (ruleSetToLoad) {
|
|
||||||
this.folderRulesService.loadRules(ruleSetToLoad, 0, 'last');
|
|
||||||
} else {
|
|
||||||
this.folderRuleSetsService.loadMoreRuleSets(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onRuleEnabledToggle(rule: Rule, isEnabled: boolean) {
|
async onRuleEnabledToggle(rule: Rule, isEnabled: boolean) {
|
||||||
const ruleSet = this.folderRuleSetsService.getRuleSetFromRuleId(rule.id);
|
await this.folderRulesService.updateRule(this.nodeId, rule.id, { ...rule, isEnabled });
|
||||||
await this.folderRulesService.updateRule(this.nodeId, rule.id, { ...rule, isEnabled }, ruleSet.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onRuleDeleteButtonClicked(rule: Rule) {
|
onRuleDeleteButtonClicked(rule: Rule) {
|
||||||
@ -174,25 +169,18 @@ export class ManageRulesSmartComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onRuleDelete(deletedRuleId: string) {
|
onRuleDelete(deletedRuleId: string) {
|
||||||
if (deletedRuleId) {
|
this.folderRuleSetsService.removeRuleFromMainRuleSet(deletedRuleId);
|
||||||
const folderToRefresh = this.folderRuleSetsService.getRuleSetFromRuleId(deletedRuleId);
|
|
||||||
if (folderToRefresh?.rules.length > 1) {
|
|
||||||
this.folderRulesService.loadRules(folderToRefresh, 0, 'first');
|
|
||||||
} else {
|
|
||||||
this.folderRuleSetsService.loadRuleSets(this.nodeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onNavigateToOtherFolder(nodeId) {
|
|
||||||
this.store.dispatch(new NavigateRouteAction(['nodes', nodeId, 'rules']));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoadMoreRuleSets() {
|
onLoadMoreRuleSets() {
|
||||||
this.folderRuleSetsService.loadMoreRuleSets();
|
this.folderRuleSetsService.loadMoreInheritedRuleSets();
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoadMoreRules(ruleSet: RuleSet) {
|
onLoadMoreRules(ruleSet: RuleSet) {
|
||||||
this.folderRulesService.loadRules(ruleSet);
|
this.folderRulesService.loadRules(ruleSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canEditRule(ruleSet: RuleSet): boolean {
|
||||||
|
return !ruleSet || FolderRuleSetsService.isOwnedRuleSet(ruleSet, this.nodeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,15 @@ export const getRuleSetsResponseMock = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDefaultRuleSetResponseMock = {
|
||||||
|
entry: {
|
||||||
|
linkedToBy: [],
|
||||||
|
owningFolder: owningFolderIdMock,
|
||||||
|
isLinkedTo: false,
|
||||||
|
id: 'rule-set-no-links'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const ruleSetMock = (rules: Rule[] = []): RuleSet => ({
|
export const ruleSetMock = (rules: Rule[] = []): RuleSet => ({
|
||||||
id: 'rule-set-id',
|
id: 'rule-set-id',
|
||||||
isLinkedTo: false,
|
isLinkedTo: false,
|
||||||
@ -76,7 +85,7 @@ export const ruleSetMock = (rules: Rule[] = []): RuleSet => ({
|
|||||||
loadingRules: false
|
loadingRules: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const ruleSetWithNoLinksMock: RuleSet = {
|
export const ownedRuleSetMock: RuleSet = {
|
||||||
id: 'rule-set-no-links',
|
id: 'rule-set-no-links',
|
||||||
isLinkedTo: false,
|
isLinkedTo: false,
|
||||||
owningFolder: owningFolderMock,
|
owningFolder: owningFolderMock,
|
||||||
@ -86,7 +95,7 @@ const ruleSetWithNoLinksMock: RuleSet = {
|
|||||||
loadingRules: false
|
loadingRules: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const ruleSetWithLinkMock: RuleSet = {
|
export const ruleSetWithLinkMock: RuleSet = {
|
||||||
id: 'rule-set-with-link',
|
id: 'rule-set-with-link',
|
||||||
isLinkedTo: true,
|
isLinkedTo: true,
|
||||||
owningFolder: otherFolderMock,
|
owningFolder: otherFolderMock,
|
||||||
@ -96,7 +105,7 @@ const ruleSetWithLinkMock: RuleSet = {
|
|||||||
loadingRules: false
|
loadingRules: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const inheritedRuleSetMock: RuleSet = {
|
export const inheritedRuleSetMock: RuleSet = {
|
||||||
id: 'inherited-rule-set',
|
id: 'inherited-rule-set',
|
||||||
isLinkedTo: false,
|
isLinkedTo: false,
|
||||||
owningFolder: otherFolderMock,
|
owningFolder: otherFolderMock,
|
||||||
@ -106,4 +115,4 @@ const inheritedRuleSetMock: RuleSet = {
|
|||||||
loadingRules: false
|
loadingRules: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ruleSetsMock: RuleSet[] = [inheritedRuleSetMock, ruleSetWithNoLinksMock, ruleSetWithLinkMock];
|
export const ruleSetsMock: RuleSet[] = [inheritedRuleSetMock, ownedRuleSetMock, ruleSetWithLinkMock];
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Rule } from '../model/rule.model';
|
import { Rule } from '../model/rule.model';
|
||||||
|
import { RuleGroupingItem } from '../model/rule-grouping-item.model';
|
||||||
|
|
||||||
export const getRulesResponseMock = {
|
export const getRulesResponseMock = {
|
||||||
list: {
|
list: {
|
||||||
@ -158,3 +159,14 @@ export const manyRulesMock: Rule[] = [ruleMock('rule1'), ruleMock('rule2'), rule
|
|||||||
export const ownedRulesMock: Rule[] = [ruleMock('owned-rule-1'), ruleMock('owned-rule-2')];
|
export const ownedRulesMock: Rule[] = [ruleMock('owned-rule-1'), ruleMock('owned-rule-2')];
|
||||||
export const linkedRulesMock: Rule[] = [ruleMock('linked-rule-1'), ruleMock('linked-rule-2')];
|
export const linkedRulesMock: Rule[] = [ruleMock('linked-rule-1'), ruleMock('linked-rule-2')];
|
||||||
export const inheritedRulesMock: Rule[] = [ruleMock('inherited-rule-1'), ruleMock('inherited-rule-2')];
|
export const inheritedRulesMock: Rule[] = [ruleMock('inherited-rule-1'), ruleMock('inherited-rule-2')];
|
||||||
|
|
||||||
|
export const ruleListGroupingItemsMock: RuleGroupingItem[] = [
|
||||||
|
{
|
||||||
|
type: 'rule',
|
||||||
|
rule: ruleMock('rule1')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'rule',
|
||||||
|
rule: ruleMock('rule2')
|
||||||
|
}
|
||||||
|
];
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Rule } from './rule.model';
|
||||||
|
import { RuleSet } from './rule-set.model';
|
||||||
|
|
||||||
|
export type RuleGroupingItem =
|
||||||
|
| {
|
||||||
|
type: 'rule';
|
||||||
|
rule: Rule;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'load-more-rules';
|
||||||
|
ruleSet: RuleSet;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'loading' | 'load-more-rule-sets';
|
||||||
|
};
|
@ -0,0 +1,47 @@
|
|||||||
|
<ng-container *ngFor="let item of items">
|
||||||
|
|
||||||
|
<aca-rule-list-item
|
||||||
|
*ngIf="item.type === 'rule'; else loadMoreRules"
|
||||||
|
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||||
|
tabindex="0"
|
||||||
|
[rule]="item.rule"
|
||||||
|
[isSelected]="isSelected(item.rule)"
|
||||||
|
(click)="onRuleClicked(item.rule)"
|
||||||
|
(enabledChanged)="onEnabledChanged(item.rule, $event)">
|
||||||
|
</aca-rule-list-item>
|
||||||
|
|
||||||
|
<ng-template #loadMoreRules>
|
||||||
|
<div
|
||||||
|
*ngIf="item.type === 'load-more-rules'; else loadMoreRuleSets"
|
||||||
|
tabindex="0"
|
||||||
|
class="aca-rule-list-grouping__non-rule-item load-more"
|
||||||
|
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||||
|
(click)="onClickLoadMoreRules(item.ruleSet)"
|
||||||
|
(keyup.enter)="onClickLoadMoreRules(item.ruleSet)">
|
||||||
|
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOAD_MORE_RULES' | translate }}
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #loadMoreRuleSets>
|
||||||
|
<div
|
||||||
|
*ngIf="item.type === 'load-more-rule-sets'; else loadingRules"
|
||||||
|
tabindex="0"
|
||||||
|
class="aca-rule-list-grouping__non-rule-item load-more"
|
||||||
|
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||||
|
(click)="onClickLoadMoreRuleSets()"
|
||||||
|
(keyup.enter)="onClickLoadMoreRuleSets()">
|
||||||
|
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOAD_MORE_RULE_SETS' | translate }}
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #loadingRules>
|
||||||
|
<div
|
||||||
|
tabindex="0"
|
||||||
|
class="aca-rule-list-grouping__non-rule-item"
|
||||||
|
matRipple matRippleColor="hsla(0,0%,0%,0.05)">
|
||||||
|
<mat-spinner mode="indeterminate" [diameter]="16"></mat-spinner>
|
||||||
|
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOADING_RULES' | translate }}
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
</ng-container>
|
@ -0,0 +1,27 @@
|
|||||||
|
.aca-rule-list-grouping {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&__non-rule-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--theme-disabled-text-color);
|
||||||
|
font-style: italic;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.5em 0;
|
||||||
|
|
||||||
|
&.load-more {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid var(--theme-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-spinner {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Alfresco Example Content Application
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { RuleListGroupingUiComponent } from './rule-list-grouping.ui-component';
|
||||||
|
import { ruleListGroupingItemsMock, rulesMock } from '../../mock/rules.mock';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||||
|
import { AcaFolderRulesModule } from '@alfresco/aca-folder-rules';
|
||||||
|
|
||||||
|
describe('RuleListGroupingUiComponent', () => {
|
||||||
|
let component: RuleListGroupingUiComponent;
|
||||||
|
let fixture: ComponentFixture<RuleListGroupingUiComponent>;
|
||||||
|
let debugElement: DebugElement;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [CoreTestingModule, AcaFolderRulesModule],
|
||||||
|
declarations: [RuleListGroupingUiComponent]
|
||||||
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RuleListGroupingUiComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
debugElement = fixture.debugElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the list of rules', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
|
||||||
|
component.items = ruleListGroupingItemsMock;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
|
||||||
|
|
||||||
|
expect(rules).toBeTruthy('Could not find rules');
|
||||||
|
expect(rules.length).toBe(2, 'Unexpected number of rules');
|
||||||
|
|
||||||
|
const rule = debugElement.query(By.css('.aca-rule-list-item:first-child'));
|
||||||
|
const name = rule.query(By.css('.aca-rule-list-item__header__name'));
|
||||||
|
const description = rule.query(By.css('.aca-rule-list-item__description'));
|
||||||
|
const toggleBtn = rule.query(By.css('mat-slide-toggle'));
|
||||||
|
|
||||||
|
expect(name.nativeElement.textContent).toBe(rulesMock[0].name);
|
||||||
|
expect(toggleBtn).toBeTruthy();
|
||||||
|
expect(description.nativeElement.textContent).toBe(rulesMock[0].description);
|
||||||
|
});
|
||||||
|
});
|
@ -24,84 +24,49 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||||
import { RuleSet } from '../../model/rule-set.model';
|
|
||||||
import { NodeInfo } from '@alfresco/aca-shared/store';
|
|
||||||
import { Rule } from '../../model/rule.model';
|
import { Rule } from '../../model/rule.model';
|
||||||
|
import { RuleGroupingItem } from '../../model/rule-grouping-item.model';
|
||||||
|
import { RuleSet } from '../../model/rule-set.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'aca-rule-set-list',
|
selector: 'aca-rule-list-grouping',
|
||||||
templateUrl: './rule-set-list.ui-component.html',
|
templateUrl: 'rule-list-grouping.ui-component.html',
|
||||||
styleUrls: ['./rule-set-list.ui-component.scss'],
|
styleUrls: ['rule-list-grouping.ui-component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
host: { class: 'aca-rule-set-list' }
|
host: { class: 'aca-rule-list-grouping' }
|
||||||
})
|
})
|
||||||
export class RuleSetListUiComponent {
|
export class RuleListGroupingUiComponent {
|
||||||
@Input()
|
@Input()
|
||||||
folderId = '';
|
items: RuleGroupingItem[] = [];
|
||||||
private _ruleSets: RuleSet[] = [];
|
|
||||||
@Input()
|
@Input()
|
||||||
get ruleSets(): RuleSet[] {
|
selectedRule: Rule = null;
|
||||||
return this._ruleSets;
|
|
||||||
}
|
|
||||||
set ruleSets(value: RuleSet[]) {
|
|
||||||
this._ruleSets = value;
|
|
||||||
this.expandedRuleSets = [...value];
|
|
||||||
}
|
|
||||||
@Input()
|
|
||||||
hasMoreRuleSets = false;
|
|
||||||
@Input()
|
|
||||||
ruleSetsLoading = false;
|
|
||||||
@Input()
|
|
||||||
selectedRule = null;
|
|
||||||
|
|
||||||
@Output()
|
|
||||||
navigateToOtherFolder = new EventEmitter<string>();
|
|
||||||
@Output()
|
|
||||||
loadMoreRuleSets = new EventEmitter<void>();
|
|
||||||
@Output()
|
|
||||||
loadMoreRules = new EventEmitter<RuleSet>();
|
|
||||||
@Output()
|
@Output()
|
||||||
selectRule = new EventEmitter<Rule>();
|
selectRule = new EventEmitter<Rule>();
|
||||||
@Output()
|
@Output()
|
||||||
ruleEnabledChanged = new EventEmitter<[Rule, boolean]>();
|
ruleEnabledChanged = new EventEmitter<[Rule, boolean]>();
|
||||||
|
@Output()
|
||||||
|
loadMoreRules = new EventEmitter<RuleSet>();
|
||||||
|
@Output()
|
||||||
|
loadMoreRuleSets = new EventEmitter<void>();
|
||||||
|
|
||||||
expandedRuleSets: RuleSet[] = [];
|
onRuleClicked(rule: Rule): void {
|
||||||
|
|
||||||
isRuleSetLinked(ruleSet: RuleSet): boolean {
|
|
||||||
return ruleSet.linkedToBy.indexOf(this.folderId) > -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
isRuleSetExpanded(ruleSet: RuleSet): boolean {
|
|
||||||
return this.expandedRuleSets.indexOf(ruleSet) > -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
clickRuleSetHeader(ruleSet: RuleSet) {
|
|
||||||
if (this.isRuleSetExpanded(ruleSet)) {
|
|
||||||
this.expandedRuleSets.splice(this.expandedRuleSets.indexOf(ruleSet), 1);
|
|
||||||
} else {
|
|
||||||
this.expandedRuleSets.push(ruleSet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clickNavigateButton(folder: NodeInfo) {
|
|
||||||
if (folder && folder.id) {
|
|
||||||
this.navigateToOtherFolder.emit(folder.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clickLoadMoreRuleSets() {
|
|
||||||
this.loadMoreRuleSets.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
clickLoadMoreRules(ruleSet: RuleSet) {
|
|
||||||
this.loadMoreRules.emit(ruleSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
onSelectRule(rule: Rule) {
|
|
||||||
this.selectRule.emit(rule);
|
this.selectRule.emit(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRuleEnabledChanged(event: [Rule, boolean]) {
|
isSelected(rule): boolean {
|
||||||
this.ruleEnabledChanged.emit(event);
|
return rule.id === this.selectedRule?.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnabledChanged(rule: Rule, isEnabled: boolean) {
|
||||||
|
this.ruleEnabledChanged.emit([rule, isEnabled]);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickLoadMoreRules(ruleSet: RuleSet) {
|
||||||
|
this.loadMoreRules.emit(ruleSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickLoadMoreRuleSets() {
|
||||||
|
this.loadMoreRuleSets.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,11 @@
|
|||||||
<div class="aca-rule-list-item__header">
|
<div class="aca-rule-list-item__header">
|
||||||
|
|
||||||
<span class="aca-rule-list-item__header__name">{{ rule.name }}</span>
|
<span class="aca-rule-list-item__header__name">{{ rule.name }}</span>
|
||||||
<mat-slide-toggle [(ngModel)]="rule.isEnabled" (click)="onToggleClick(!rule.isEnabled, $event)"></mat-slide-toggle>
|
|
||||||
|
<mat-slide-toggle
|
||||||
|
[checked]="rule.isEnabled"
|
||||||
|
(click)="onToggleClick(!rule.isEnabled, $event)">
|
||||||
|
</mat-slide-toggle>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="aca-rule-list-item__description">{{ rule.description }}</div>
|
<div class="aca-rule-list-item__description">{{ rule.description }}</div>
|
||||||
|
@ -45,6 +45,7 @@ export class RuleListItemUiComponent {
|
|||||||
|
|
||||||
onToggleClick(isEnabled: boolean, event: Event) {
|
onToggleClick(isEnabled: boolean, event: Event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
this.rule.isEnabled = !this.rule.isEnabled;
|
||||||
this.enabledChanged.emit(isEnabled);
|
this.enabledChanged.emit(isEnabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,80 @@
|
|||||||
<div class="aca-rules-list" >
|
<div
|
||||||
<aca-rule-list-item
|
*ngIf="inheritedRuleSetGroupingItems.length > 0"
|
||||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
class="aca-rule-list__item"
|
||||||
tabindex="0"
|
data-automation-id="rule-list-item"
|
||||||
*ngFor="let rule of rules"
|
[ngClass]="{ expanded: inheritedRuleSetsExpanded }">
|
||||||
[rule]="rule"
|
|
||||||
[isSelected]="isSelected(rule)"
|
<div class="aca-rule-list__item__header">
|
||||||
(click)="onRuleClicked(rule)"
|
|
||||||
(enabledChanged)="onEnabledChanged(rule, $event)">
|
<div
|
||||||
</aca-rule-list-item>
|
tabindex="0"
|
||||||
|
class="aca-rule-list__item__header__title"
|
||||||
|
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||||
|
(click)="inheritedRuleSetsExpanded = !inheritedRuleSetsExpanded"
|
||||||
|
(keyup.enter)="inheritedRuleSetsExpanded = !inheritedRuleSetsExpanded">
|
||||||
|
|
||||||
|
<span class="aca-rule-list__item__header__title__text">
|
||||||
|
{{ 'ACA_FOLDER_RULES.RULE_LIST.INHERITED_RULES' | translate }}
|
||||||
|
<mat-icon [matTooltip]="'ACA_FOLDER_RULES.RULE_LIST.INHERITED_RULES_WILL_BE_RUN_FIRST' | translate">
|
||||||
|
info
|
||||||
|
</mat-icon>
|
||||||
|
</span>
|
||||||
|
<mat-icon class="aca-rule-list__item__header__icon">
|
||||||
|
{{ inheritedRuleSetsExpanded ? 'expand_more' : 'chevron_right' }}
|
||||||
|
</mat-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aca-rule-list-grouping
|
||||||
|
*ngIf="inheritedRuleSetsExpanded"
|
||||||
|
[items]="inheritedRuleSetGroupingItems"
|
||||||
|
[selectedRule]="selectedRule"
|
||||||
|
(selectRule)="onSelectRule($event)"
|
||||||
|
(ruleEnabledChanged)="onRuleEnabledChanged($event)"
|
||||||
|
(loadMoreRules)="onLoadMoreRules($event)"
|
||||||
|
(loadMoreRuleSets)="onLoadMoreRuleSets()">
|
||||||
|
</aca-rule-list-grouping>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
*ngIf="mainRuleSetGroupingItems.length > 0"
|
||||||
|
class="aca-rule-list__item"
|
||||||
|
data-automation-id="rule-list-item"
|
||||||
|
[ngClass]="{ expanded: mainRuleSetExpanded }">
|
||||||
|
|
||||||
|
<div class="aca-rule-list__item__header">
|
||||||
|
|
||||||
|
<div
|
||||||
|
tabindex="0"
|
||||||
|
class="aca-rule-list__item__header__title"
|
||||||
|
data-automation-id="main-rule-set-title"
|
||||||
|
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
||||||
|
(click)="mainRuleSetExpanded = !mainRuleSetExpanded"
|
||||||
|
(keyup.enter)="mainRuleSetExpanded = !mainRuleSetExpanded">
|
||||||
|
|
||||||
|
<ng-container *ngIf="isMainRuleSetOwned; else linkedRuleSet">
|
||||||
|
{{ 'ACA_FOLDER_RULES.RULE_LIST.OWNED_RULES' | translate }}
|
||||||
|
</ng-container>
|
||||||
|
<ng-template #linkedRuleSet>
|
||||||
|
{{ 'ACA_FOLDER_RULES.RULE_LIST.LINKED_RULES' | translate }}
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<mat-icon class="aca-rule-list__item__header__icon">
|
||||||
|
{{ mainRuleSetExpanded ? 'expand_more' : 'chevron_right' }}
|
||||||
|
</mat-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aca-rule-list-grouping
|
||||||
|
*ngIf="mainRuleSetExpanded"
|
||||||
|
[items]="mainRuleSetGroupingItems"
|
||||||
|
[selectedRule]="selectedRule"
|
||||||
|
(selectRule)="onSelectRule($event)"
|
||||||
|
(ruleEnabledChanged)="onRuleEnabledChanged($event)"
|
||||||
|
(loadMoreRules)="onLoadMoreRules($event)">
|
||||||
|
</aca-rule-list-grouping>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,52 @@
|
|||||||
.aca-rule-list {
|
.aca-rule-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid var(--theme-border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--theme-text-color);
|
||||||
|
user-select: none;
|
||||||
|
font-size: 0.9em;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.mat-icon {
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.expanded {
|
||||||
|
.aca-rule-list__item__header {
|
||||||
|
border-bottom: 1px solid var(--theme-border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,49 +23,50 @@
|
|||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { RuleListUiComponent } from './rule-list.ui-component';
|
import { RuleListUiComponent } from './rule-list.ui-component';
|
||||||
import { rulesMock } from '../../mock/rules.mock';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { CoreTestingModule } from '@alfresco/adf-core';
|
||||||
|
import { RuleListGroupingUiComponent } from '../rule-list-grouping/rule-list-grouping.ui-component';
|
||||||
|
import { RuleListItemUiComponent } from '../rule-list-item/rule-list-item.ui-component';
|
||||||
|
import { ownedRuleSetMock, ruleSetsMock, ruleSetWithLinkMock } from '../../mock/rule-sets.mock';
|
||||||
import { DebugElement } from '@angular/core';
|
import { DebugElement } from '@angular/core';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
import { owningFolderIdMock } from '../../mock/node.mock';
|
||||||
import { AcaFolderRulesModule } from '@alfresco/aca-folder-rules';
|
|
||||||
|
|
||||||
describe('RuleListUiComponent', () => {
|
describe('RuleListUiComponent', () => {
|
||||||
let component: RuleListUiComponent;
|
|
||||||
let fixture: ComponentFixture<RuleListUiComponent>;
|
let fixture: ComponentFixture<RuleListUiComponent>;
|
||||||
|
let component: RuleListUiComponent;
|
||||||
let debugElement: DebugElement;
|
let debugElement: DebugElement;
|
||||||
|
|
||||||
|
const innerTextWithoutIcon = (element: HTMLDivElement): string => element.innerText.replace(/(expand_more|chevron_right)$/, '').trim();
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [CoreTestingModule, AcaFolderRulesModule],
|
imports: [CoreTestingModule],
|
||||||
declarations: [RuleListUiComponent]
|
declarations: [RuleListUiComponent, RuleListGroupingUiComponent, RuleListItemUiComponent]
|
||||||
});
|
});
|
||||||
|
|
||||||
fixture = TestBed.createComponent(RuleListUiComponent);
|
fixture = TestBed.createComponent(RuleListUiComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
debugElement = fixture.debugElement;
|
debugElement = fixture.debugElement;
|
||||||
|
|
||||||
|
component.folderId = owningFolderIdMock;
|
||||||
|
component.inheritedRuleSets = ruleSetsMock;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display the list of rules', () => {
|
it('should show "Rules from current folder" as a title if the main rule set is owned', () => {
|
||||||
expect(component).toBeTruthy();
|
component.mainRuleSet = ownedRuleSetMock;
|
||||||
|
|
||||||
component.rules = rulesMock;
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const rules = debugElement.queryAll(By.css('.aca-rule-list-item'));
|
const mainRuleSetTitleElement = debugElement.query(By.css(`[data-automation-id="main-rule-set-title"]`));
|
||||||
|
expect(innerTextWithoutIcon(mainRuleSetTitleElement.nativeElement as HTMLDivElement)).toBe('ACA_FOLDER_RULES.RULE_LIST.OWNED_RULES');
|
||||||
|
});
|
||||||
|
|
||||||
expect(rules).toBeTruthy('Could not find rules');
|
it('should show "Rules from linked folder" as a title if the main rule set is linked', () => {
|
||||||
expect(rules.length).toBe(2, 'Unexpected number of rules');
|
component.mainRuleSet = ruleSetWithLinkMock;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
const rule = debugElement.query(By.css('.aca-rule-list-item:first-child'));
|
const mainRuleSetTitleElement = debugElement.query(By.css(`[data-automation-id="main-rule-set-title"]`));
|
||||||
const name = rule.query(By.css('.aca-rule-list-item__header__name'));
|
expect(innerTextWithoutIcon(mainRuleSetTitleElement.nativeElement as HTMLDivElement)).toBe('ACA_FOLDER_RULES.RULE_LIST.LINKED_RULES');
|
||||||
const description = rule.query(By.css('.aca-rule-list-item__description'));
|
|
||||||
const toggleBtn = rule.query(By.css('mat-slide-toggle'));
|
|
||||||
|
|
||||||
expect(name.nativeElement.textContent).toBe(rulesMock[0].name);
|
|
||||||
expect(toggleBtn).toBeTruthy();
|
|
||||||
expect(description.nativeElement.textContent).toBe(rulesMock[0].description);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -24,35 +24,98 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
|
||||||
|
import { RuleSet } from '../../model/rule-set.model';
|
||||||
import { Rule } from '../../model/rule.model';
|
import { Rule } from '../../model/rule.model';
|
||||||
|
import { RuleGroupingItem } from '../../model/rule-grouping-item.model';
|
||||||
|
import { FolderRuleSetsService } from '../../services/folder-rule-sets.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'aca-rule-list',
|
selector: 'aca-rule-list',
|
||||||
templateUrl: 'rule-list.ui-component.html',
|
templateUrl: './rule-list.ui-component.html',
|
||||||
styleUrls: ['rule-list.ui-component.scss'],
|
styleUrls: ['./rule-list.ui-component.scss'],
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
host: { class: 'aca-rule-list' }
|
host: { class: 'aca-rule-list' }
|
||||||
})
|
})
|
||||||
export class RuleListUiComponent {
|
export class RuleListUiComponent {
|
||||||
@Input()
|
@Input()
|
||||||
rules: Rule[] = [];
|
mainRuleSet: RuleSet = null;
|
||||||
@Input()
|
@Input()
|
||||||
selectedRule: Rule = null;
|
folderId: string;
|
||||||
|
@Input()
|
||||||
|
inheritedRuleSets: RuleSet[] = [];
|
||||||
|
@Input()
|
||||||
|
hasMoreRuleSets = false;
|
||||||
|
@Input()
|
||||||
|
ruleSetsLoading = false;
|
||||||
|
@Input()
|
||||||
|
selectedRule = null;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
loadMoreRuleSets = new EventEmitter<void>();
|
||||||
|
@Output()
|
||||||
|
loadMoreRules = new EventEmitter<RuleSet>();
|
||||||
@Output()
|
@Output()
|
||||||
selectRule = new EventEmitter<Rule>();
|
selectRule = new EventEmitter<Rule>();
|
||||||
@Output()
|
@Output()
|
||||||
ruleEnabledChanged = new EventEmitter<[Rule, boolean]>();
|
ruleEnabledChanged = new EventEmitter<[Rule, boolean]>();
|
||||||
|
|
||||||
onRuleClicked(rule: Rule): void {
|
inheritedRuleSetsExpanded = true;
|
||||||
|
mainRuleSetExpanded = true;
|
||||||
|
|
||||||
|
get isMainRuleSetOwned(): boolean {
|
||||||
|
return FolderRuleSetsService.isOwnedRuleSet(this.mainRuleSet, this.folderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
get mainRuleSetGroupingItems(): RuleGroupingItem[] {
|
||||||
|
return this.mainRuleSet ? this.getRuleSetGroupingItems(this.mainRuleSet) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
get inheritedRuleSetGroupingItems(): RuleGroupingItem[] {
|
||||||
|
const items = this.inheritedRuleSets.reduce((accumulator: RuleGroupingItem[], currentRuleSet: RuleSet) => {
|
||||||
|
accumulator.push(...this.getRuleSetGroupingItems(currentRuleSet));
|
||||||
|
return accumulator;
|
||||||
|
}, []);
|
||||||
|
if (this.ruleSetsLoading || this.hasMoreRuleSets) {
|
||||||
|
items.push({
|
||||||
|
type: this.ruleSetsLoading ? 'loading' : 'load-more-rule-sets'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRuleSetGroupingItems(ruleSet: RuleSet): RuleGroupingItem[] {
|
||||||
|
const items: RuleGroupingItem[] = ruleSet.rules.map((rule: Rule) => ({
|
||||||
|
type: 'rule',
|
||||||
|
rule
|
||||||
|
}));
|
||||||
|
if (ruleSet.loadingRules || ruleSet.hasMoreRules) {
|
||||||
|
items.push(
|
||||||
|
ruleSet.loadingRules
|
||||||
|
? {
|
||||||
|
type: 'loading'
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
type: 'load-more-rules',
|
||||||
|
ruleSet
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadMoreRuleSets() {
|
||||||
|
this.loadMoreRuleSets.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoadMoreRules(ruleSet: RuleSet) {
|
||||||
|
this.loadMoreRules.emit(ruleSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelectRule(rule: Rule) {
|
||||||
this.selectRule.emit(rule);
|
this.selectRule.emit(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelected(rule): boolean {
|
onRuleEnabledChanged(event: [Rule, boolean]) {
|
||||||
return rule.id === this.selectedRule?.id;
|
this.ruleEnabledChanged.emit(event);
|
||||||
}
|
|
||||||
|
|
||||||
onEnabledChanged(rule: Rule, isEnabled: boolean) {
|
|
||||||
this.ruleEnabledChanged.emit([rule, isEnabled]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
<div
|
|
||||||
*ngFor="let ruleSet of ruleSets"
|
|
||||||
class="aca-rule-set-list__item"
|
|
||||||
data-automation-id="rule-set-list-item"
|
|
||||||
[ngClass]="{ expanded: isRuleSetExpanded(ruleSet) }">
|
|
||||||
|
|
||||||
<div class="aca-rule-set-list__item__header">
|
|
||||||
|
|
||||||
<div
|
|
||||||
tabindex="0"
|
|
||||||
*ngIf="ruleSet.owningFolder.id !== folderId"
|
|
||||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
|
||||||
class="aca-rule-set-list__item__header__navigate-button"
|
|
||||||
(click)="clickNavigateButton(ruleSet.owningFolder)"
|
|
||||||
(keyup.enter)="clickNavigateButton(ruleSet.owningFolder)">
|
|
||||||
<mat-icon>edit_note</mat-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
tabindex="0"
|
|
||||||
class="aca-rule-set-list__item__header__title"
|
|
||||||
data-automation-id="rule-set-item-title"
|
|
||||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
|
||||||
(click)="clickRuleSetHeader(ruleSet)"
|
|
||||||
(keyup.enter)="clickRuleSetHeader(ruleSet)">
|
|
||||||
|
|
||||||
<ng-container *ngIf="ruleSet.owningFolder.id === folderId; else nonOwnedRuleSet">
|
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.OWNED_BY_THIS_FOLDER' | translate }}
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-template #nonOwnedRuleSet>
|
|
||||||
<ng-container *ngIf="isRuleSetLinked(ruleSet); else inheritedRuleSet">
|
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.LINKED_FROM' | translate }} {{ ruleSet.owningFolder.name }}
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<ng-template #inheritedRuleSet>
|
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.INHERITED_FROM' | translate }} {{ ruleSet.owningFolder.name }}
|
|
||||||
</ng-template>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<mat-icon class="aca-rule-set-list__item__header__icon">
|
|
||||||
{{ isRuleSetExpanded(ruleSet) ? 'expand_more' : 'chevron_right' }}
|
|
||||||
</mat-icon>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ng-container *ngIf="isRuleSetExpanded(ruleSet)">
|
|
||||||
<aca-rule-list
|
|
||||||
[rules]="ruleSet.rules"
|
|
||||||
[selectedRule]="selectedRule"
|
|
||||||
(selectRule)="onSelectRule($event)"
|
|
||||||
(ruleEnabledChanged)="onRuleEnabledChanged($event)">
|
|
||||||
</aca-rule-list>
|
|
||||||
|
|
||||||
<div
|
|
||||||
*ngIf="ruleSet.hasMoreRules || ruleSet.loadingRules"
|
|
||||||
tabindex="0"
|
|
||||||
class="aca-rule-set-list__item__load-more load-more"
|
|
||||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
|
||||||
(click)="clickLoadMoreRules(ruleSet)"
|
|
||||||
(keyup.enter)="clickLoadMoreRules(ruleSet)">
|
|
||||||
<ng-container *ngIf="!ruleSet.loadingRules; else rulesLoadingTemplate">
|
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOAD_MORE_RULES' | translate }}
|
|
||||||
</ng-container>
|
|
||||||
<ng-template #rulesLoadingTemplate>
|
|
||||||
<mat-spinner mode="indeterminate" [diameter]="16"></mat-spinner>
|
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOADING_RULES' | translate }}
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
*ngIf="hasMoreRuleSets"
|
|
||||||
tabindex="0"
|
|
||||||
class="aca-rule-set-list__load-more load-more"
|
|
||||||
matRipple matRippleColor="hsla(0,0%,0%,0.05)"
|
|
||||||
[matRippleDisabled]="ruleSetsLoading"
|
|
||||||
(click)="clickLoadMoreRuleSets()"
|
|
||||||
(keyup.enter)="clickLoadMoreRuleSets()">
|
|
||||||
<ng-container *ngIf="!ruleSetsLoading; else ruleSetsLoadingTemplate">
|
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOAD_MORE_RULE_SETS' | translate }}
|
|
||||||
</ng-container>
|
|
||||||
<ng-template #ruleSetsLoadingTemplate>
|
|
||||||
<mat-spinner mode="indeterminate" [diameter]="16"></mat-spinner>
|
|
||||||
{{ 'ACA_FOLDER_RULES.RULE_LIST.LOADING_RULE_SETS' | translate }}
|
|
||||||
</ng-template>
|
|
||||||
</div>
|
|
@ -1,78 +0,0 @@
|
|||||||
.aca-rule-set-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow-y: auto;
|
|
||||||
gap: 8px;
|
|
||||||
|
|
||||||
&__item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border: 1px solid var(--theme-border-color);
|
|
||||||
border-radius: 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&__header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: stretch;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--theme-text-color);
|
|
||||||
user-select: none;
|
|
||||||
font-size: 0.9em;
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
padding: 0.5em 1em;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__navigate-button {
|
|
||||||
border-right: 1px solid var(--theme-border-color);
|
|
||||||
padding: 0.5em;
|
|
||||||
|
|
||||||
mat-icon {
|
|
||||||
transform: scale(0.8);
|
|
||||||
transform-origin: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__load-more {
|
|
||||||
border-top: 1px solid var(--theme-border-color);
|
|
||||||
padding: 0.5em 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.expanded {
|
|
||||||
.aca-rule-set-list__item__header {
|
|
||||||
border-bottom: 1px solid var(--theme-border-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__load-more {
|
|
||||||
padding: 1em 2em;
|
|
||||||
border: 1px solid var(--theme-border-color);
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.load-more {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
color: var(--theme-disabled-text-color);
|
|
||||||
font-style: italic;
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
.mat-spinner {
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Alfresco Example Content Application
|
|
||||||
*
|
|
||||||
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { RuleSetListUiComponent } from './rule-set-list.ui-component';
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
import { CoreTestingModule } from '@alfresco/adf-core';
|
|
||||||
import { RuleListUiComponent } from '../rule-list/rule-list.ui-component';
|
|
||||||
import { RuleListItemUiComponent } from '../rule-list-item/rule-list-item.ui-component';
|
|
||||||
import { ruleSetsMock } from '../../mock/rule-sets.mock';
|
|
||||||
import { DebugElement } from '@angular/core';
|
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { owningFolderIdMock } from '../../mock/node.mock';
|
|
||||||
|
|
||||||
describe('RuleSetListUiComponent', () => {
|
|
||||||
let fixture: ComponentFixture<RuleSetListUiComponent>;
|
|
||||||
let component: RuleSetListUiComponent;
|
|
||||||
let debugElement: DebugElement;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
imports: [CoreTestingModule],
|
|
||||||
declarations: [RuleSetListUiComponent, RuleListUiComponent, RuleListItemUiComponent]
|
|
||||||
});
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(RuleSetListUiComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
debugElement = fixture.debugElement;
|
|
||||||
|
|
||||||
component.folderId = owningFolderIdMock;
|
|
||||||
component.ruleSets = ruleSetsMock;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display a list of rule sets', () => {
|
|
||||||
const ruleSetElements = debugElement.queryAll(By.css(`[data-automation-id="rule-set-list-item"]`));
|
|
||||||
|
|
||||||
expect(ruleSetElements.length).toBe(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show the right message for the right sort of rule set', () => {
|
|
||||||
const ruleSetTitleElements = debugElement.queryAll(By.css(`[data-automation-id="rule-set-item-title"]`));
|
|
||||||
|
|
||||||
const innerTextWithoutIcon = (element: HTMLDivElement): string => element.innerText.replace(/(expand_more|chevron_right)$/, '').trim();
|
|
||||||
|
|
||||||
expect(ruleSetTitleElements.length).toBe(3);
|
|
||||||
expect(innerTextWithoutIcon(ruleSetTitleElements[0].nativeElement as HTMLDivElement)).toBe(
|
|
||||||
'ACA_FOLDER_RULES.RULE_LIST.INHERITED_FROM other-folder-name'
|
|
||||||
);
|
|
||||||
expect(innerTextWithoutIcon(ruleSetTitleElements[1].nativeElement as HTMLDivElement)).toBe('ACA_FOLDER_RULES.RULE_LIST.OWNED_BY_THIS_FOLDER');
|
|
||||||
expect(innerTextWithoutIcon(ruleSetTitleElements[2].nativeElement as HTMLDivElement)).toBe(
|
|
||||||
'ACA_FOLDER_RULES.RULE_LIST.LINKED_FROM other-folder-name'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -30,7 +30,7 @@ import { FolderRulesService } from './folder-rules.service';
|
|||||||
import { ContentApiService } from '@alfresco/aca-shared';
|
import { ContentApiService } from '@alfresco/aca-shared';
|
||||||
import { getOtherFolderEntryMock, getOwningFolderEntryMock, otherFolderIdMock, owningFolderIdMock, owningFolderMock } from '../mock/node.mock';
|
import { getOtherFolderEntryMock, getOwningFolderEntryMock, otherFolderIdMock, owningFolderIdMock, owningFolderMock } from '../mock/node.mock';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { getRuleSetsResponseMock, ruleSetsMock } from '../mock/rule-sets.mock';
|
import { getDefaultRuleSetResponseMock, getRuleSetsResponseMock, inheritedRuleSetMock, ownedRuleSetMock } from '../mock/rule-sets.mock';
|
||||||
import { take } from 'rxjs/operators';
|
import { take } from 'rxjs/operators';
|
||||||
import { inheritedRulesMock, linkedRulesMock, ownedRulesMock, ruleMock } from '../mock/rules.mock';
|
import { inheritedRulesMock, linkedRulesMock, ownedRulesMock, ruleMock } from '../mock/rules.mock';
|
||||||
|
|
||||||
@ -52,7 +52,11 @@ describe('FolderRuleSetsService', () => {
|
|||||||
folderRulesService = TestBed.inject(FolderRulesService);
|
folderRulesService = TestBed.inject(FolderRulesService);
|
||||||
contentApiService = TestBed.inject(ContentApiService);
|
contentApiService = TestBed.inject(ContentApiService);
|
||||||
|
|
||||||
callApiSpy = spyOn<any>(folderRuleSetsService, 'callApi');
|
callApiSpy = spyOn<any>(folderRuleSetsService, 'callApi')
|
||||||
|
.withArgs(`/nodes/${owningFolderIdMock}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET')
|
||||||
|
.and.returnValue(of(getDefaultRuleSetResponseMock))
|
||||||
|
.withArgs(`/nodes/${owningFolderIdMock}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=0&maxItems=100`, 'GET')
|
||||||
|
.and.returnValue(of(getRuleSetsResponseMock));
|
||||||
spyOn<any>(folderRulesService, 'getRules')
|
spyOn<any>(folderRulesService, 'getRules')
|
||||||
.withArgs(jasmine.anything(), 'rule-set-no-links')
|
.withArgs(jasmine.anything(), 'rule-set-no-links')
|
||||||
.and.returnValue(of({ rules: ownedRulesMock, hasMoreRules: false }))
|
.and.returnValue(of({ rules: ownedRulesMock, hasMoreRules: false }))
|
||||||
@ -69,7 +73,6 @@ describe('FolderRuleSetsService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it(`should load node info when loading the node's rule sets`, async () => {
|
it(`should load node info when loading the node's rule sets`, async () => {
|
||||||
callApiSpy.and.returnValue(of(getRuleSetsResponseMock));
|
|
||||||
// take(2), because: 1 = init of the BehaviourSubject, 2 = in subscribe
|
// take(2), because: 1 = init of the BehaviourSubject, 2 = in subscribe
|
||||||
const folderInfoPromise = folderRuleSetsService.folderInfo$.pipe(take(2)).toPromise();
|
const folderInfoPromise = folderRuleSetsService.folderInfo$.pipe(take(2)).toPromise();
|
||||||
|
|
||||||
@ -80,29 +83,38 @@ describe('FolderRuleSetsService', () => {
|
|||||||
expect(folderInfo).toEqual(owningFolderMock);
|
expect(folderInfo).toEqual(owningFolderMock);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load rule sets of a node', async () => {
|
it('should load the main rule set (main or linked)', async () => {
|
||||||
callApiSpy.and.returnValue(of(getRuleSetsResponseMock));
|
|
||||||
// take(3), because: 1 = init of the BehaviourSubject, 2 = reinitialise at beginning of loadRuleSets, 3 = in subscribe
|
// take(3), because: 1 = init of the BehaviourSubject, 2 = reinitialise at beginning of loadRuleSets, 3 = in subscribe
|
||||||
const ruleSetListingPromise = folderRuleSetsService.ruleSetListing$.pipe(take(3)).toPromise();
|
const mainRuleSetPromise = folderRuleSetsService.mainRuleSet$.pipe(take(3)).toPromise();
|
||||||
|
|
||||||
|
folderRuleSetsService.loadRuleSets(owningFolderIdMock);
|
||||||
|
const ruleSet = await mainRuleSetPromise;
|
||||||
|
|
||||||
|
expect(callApiSpy).toHaveBeenCalledWith(`/nodes/${owningFolderIdMock}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET');
|
||||||
|
expect(ruleSet).toEqual(ownedRuleSetMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load inherited rule sets of a node and filter out owned or inherited rule sets', async () => {
|
||||||
|
// take(3), because: 1 = init of the BehaviourSubject, 2 = reinitialise at beginning of loadRuleSets, 3 = in subscribe
|
||||||
|
const inheritedRuleSetsPromise = folderRuleSetsService.inheritedRuleSets$.pipe(take(3)).toPromise();
|
||||||
const hasMoreRuleSetsPromise = folderRuleSetsService.hasMoreRuleSets$.pipe(take(3)).toPromise();
|
const hasMoreRuleSetsPromise = folderRuleSetsService.hasMoreRuleSets$.pipe(take(3)).toPromise();
|
||||||
|
|
||||||
folderRuleSetsService.loadRuleSets(owningFolderIdMock);
|
folderRuleSetsService.loadRuleSets(owningFolderIdMock);
|
||||||
const ruleSets = await ruleSetListingPromise;
|
const ruleSets = await inheritedRuleSetsPromise;
|
||||||
const hasMoreRuleSets = await hasMoreRuleSetsPromise;
|
const hasMoreRuleSets = await hasMoreRuleSetsPromise;
|
||||||
|
|
||||||
expect(callApiSpy).toHaveBeenCalledWith(
|
expect(callApiSpy).toHaveBeenCalledWith(
|
||||||
`/nodes/${owningFolderIdMock}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=0&maxItems=100`,
|
`/nodes/${owningFolderIdMock}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=0&maxItems=100`,
|
||||||
'GET'
|
'GET'
|
||||||
);
|
);
|
||||||
expect(ruleSets).toEqual(ruleSetsMock);
|
expect(ruleSets).toEqual([inheritedRuleSetMock]);
|
||||||
expect(hasMoreRuleSets).toEqual(false);
|
expect(hasMoreRuleSets).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select the first rule of the owned rule set of the folder', async () => {
|
it('should select the first rule of the owned rule set of the folder', async () => {
|
||||||
callApiSpy.and.returnValue(of(getRuleSetsResponseMock));
|
|
||||||
const selectRuleSpy = spyOn(folderRulesService, 'selectRule');
|
const selectRuleSpy = spyOn(folderRulesService, 'selectRule');
|
||||||
// take(3), because: 1 = init of the BehaviourSubject, 2 = reinitialise at beginning of loadRuleSets, 3 = in subscribe
|
// take(3), because: 1 = init of the BehaviourSubject, 2 = reinitialise at beginning of loadRuleSets, 3 = in subscribe
|
||||||
const ruleSetListingPromise = folderRuleSetsService.ruleSetListing$.pipe(take(3)).toPromise();
|
const ruleSetListingPromise = folderRuleSetsService.inheritedRuleSets$.pipe(take(3)).toPromise();
|
||||||
|
|
||||||
folderRuleSetsService.loadRuleSets(owningFolderIdMock);
|
folderRuleSetsService.loadRuleSets(owningFolderIdMock);
|
||||||
await ruleSetListingPromise;
|
await ruleSetListingPromise;
|
||||||
|
@ -40,20 +40,46 @@ import { Rule } from '../model/rule.model';
|
|||||||
export class FolderRuleSetsService {
|
export class FolderRuleSetsService {
|
||||||
public static MAX_RULE_SETS_PER_GET = 100;
|
public static MAX_RULE_SETS_PER_GET = 100;
|
||||||
|
|
||||||
|
static isOwnedRuleSet(ruleSet: RuleSet, nodeId: string): boolean {
|
||||||
|
return ruleSet.owningFolder.id === nodeId;
|
||||||
|
}
|
||||||
|
static isLinkedRuleSet(ruleSet: RuleSet, nodeId: string): boolean {
|
||||||
|
return ruleSet.linkedToBy.indexOf(nodeId) > -1;
|
||||||
|
}
|
||||||
|
static isMainRuleSet(ruleSet: RuleSet, nodeId: string): boolean {
|
||||||
|
return this.isOwnedRuleSet(ruleSet, nodeId) || this.isLinkedRuleSet(ruleSet, nodeId);
|
||||||
|
}
|
||||||
|
static isInheritedRuleSet(ruleSet: RuleSet, nodeId: string): boolean {
|
||||||
|
return !this.isMainRuleSet(ruleSet, nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
private currentFolder: NodeInfo = null;
|
private currentFolder: NodeInfo = null;
|
||||||
private ruleSets: RuleSet[] = [];
|
private mainRuleSet: RuleSet = null;
|
||||||
|
private inheritedRuleSets: RuleSet[] = [];
|
||||||
private hasMoreRuleSets = true;
|
private hasMoreRuleSets = true;
|
||||||
|
|
||||||
private ruleSetListingSource = new BehaviorSubject<RuleSet[]>([]);
|
private mainRuleSetSource = new BehaviorSubject<RuleSet>(null);
|
||||||
|
private inheritedRuleSetsSource = new BehaviorSubject<RuleSet[]>([]);
|
||||||
private hasMoreRuleSetsSource = new BehaviorSubject<boolean>(true);
|
private hasMoreRuleSetsSource = new BehaviorSubject<boolean>(true);
|
||||||
private folderInfoSource = new BehaviorSubject<NodeInfo>(null);
|
private folderInfoSource = new BehaviorSubject<NodeInfo>(null);
|
||||||
private isLoadingSource = new BehaviorSubject<boolean>(false);
|
private isLoadingSource = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
ruleSetListing$: Observable<RuleSet[]> = this.ruleSetListingSource.asObservable();
|
mainRuleSet$: Observable<RuleSet> = this.mainRuleSetSource.asObservable();
|
||||||
|
inheritedRuleSets$: Observable<RuleSet[]> = this.inheritedRuleSetsSource.asObservable();
|
||||||
hasMoreRuleSets$: Observable<boolean> = this.hasMoreRuleSetsSource.asObservable();
|
hasMoreRuleSets$: Observable<boolean> = this.hasMoreRuleSetsSource.asObservable();
|
||||||
folderInfo$: Observable<NodeInfo> = this.folderInfoSource.asObservable();
|
folderInfo$: Observable<NodeInfo> = this.folderInfoSource.asObservable();
|
||||||
isLoading$ = this.isLoadingSource.asObservable();
|
isLoading$ = this.isLoadingSource.asObservable();
|
||||||
|
|
||||||
|
selectedRuleSet$ = this.folderRulesService.selectedRule$.pipe(
|
||||||
|
map((rule: Rule) => {
|
||||||
|
if (this.mainRuleSet?.rules.findIndex((r: Rule) => r.id === rule.id) > -1) {
|
||||||
|
return this.mainRuleSet;
|
||||||
|
} else {
|
||||||
|
return this.inheritedRuleSets.find((ruleSet: RuleSet) => ruleSet.rules.findIndex((r: Rule) => r.id === rule.id) > -1) ?? null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
constructor(private apiService: AlfrescoApiService, private contentApi: ContentApiService, private folderRulesService: FolderRulesService) {}
|
constructor(private apiService: AlfrescoApiService, private contentApi: ContentApiService, private folderRulesService: FolderRulesService) {}
|
||||||
|
|
||||||
private callApi(path: string, httpMethod: string, body: object = {}): Promise<any> {
|
private callApi(path: string, httpMethod: string, body: object = {}): Promise<any> {
|
||||||
@ -62,7 +88,19 @@ export class FolderRuleSetsService {
|
|||||||
return this.apiService.getInstance().contentPrivateClient.callApi(path, httpMethod, ...params);
|
return this.apiService.getInstance().contentPrivateClient.callApi(path, httpMethod, ...params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRuleSets(nodeId: string, skipCount = 0): Observable<RuleSet[]> {
|
private getMainRuleSet(nodeId: string): Observable<RuleSet> {
|
||||||
|
return from(this.callApi(`/nodes/${nodeId}/rule-sets/-default-?include=isLinkedTo,owningFolder,linkedToBy`, 'GET')).pipe(
|
||||||
|
catchError((error) => {
|
||||||
|
if (error.status === 404) {
|
||||||
|
return of({ entry: null });
|
||||||
|
}
|
||||||
|
return of(error);
|
||||||
|
}),
|
||||||
|
switchMap((res) => this.formatRuleSet(res.entry))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getInheritedRuleSets(nodeId: string, skipCount = 0): Observable<RuleSet[]> {
|
||||||
return from(
|
return from(
|
||||||
this.callApi(
|
this.callApi(
|
||||||
`/nodes/${nodeId}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=${skipCount}&maxItems=${FolderRuleSetsService.MAX_RULE_SETS_PER_GET}`,
|
`/nodes/${nodeId}/rule-sets?include=isLinkedTo,owningFolder,linkedToBy&skipCount=${skipCount}&maxItems=${FolderRuleSetsService.MAX_RULE_SETS_PER_GET}`,
|
||||||
@ -74,16 +112,19 @@ export class FolderRuleSetsService {
|
|||||||
this.hasMoreRuleSets = res.list.pagination.hasMoreItems;
|
this.hasMoreRuleSets = res.list.pagination.hasMoreItems;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
switchMap((res) => this.formatRuleSets(res))
|
switchMap((res) => this.formatRuleSets(res)),
|
||||||
|
map((ruleSets: RuleSet[]) => ruleSets.filter((ruleSet) => FolderRuleSetsService.isInheritedRuleSet(ruleSet, this.currentFolder.id)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadRuleSets(nodeId: string) {
|
loadRuleSets(nodeId: string) {
|
||||||
this.isLoadingSource.next(true);
|
this.isLoadingSource.next(true);
|
||||||
this.ruleSets = [];
|
this.mainRuleSet = null;
|
||||||
|
this.inheritedRuleSets = [];
|
||||||
this.hasMoreRuleSets = true;
|
this.hasMoreRuleSets = true;
|
||||||
this.currentFolder = null;
|
this.currentFolder = null;
|
||||||
this.ruleSetListingSource.next(this.ruleSets);
|
this.mainRuleSetSource.next(this.mainRuleSet);
|
||||||
|
this.inheritedRuleSetsSource.next(this.inheritedRuleSets);
|
||||||
this.hasMoreRuleSetsSource.next(this.hasMoreRuleSets);
|
this.hasMoreRuleSetsSource.next(this.hasMoreRuleSets);
|
||||||
this.getNodeInfo(nodeId)
|
this.getNodeInfo(nodeId)
|
||||||
.pipe(
|
.pipe(
|
||||||
@ -91,29 +132,27 @@ export class FolderRuleSetsService {
|
|||||||
this.currentFolder = nodeInfo;
|
this.currentFolder = nodeInfo;
|
||||||
this.folderInfoSource.next(this.currentFolder);
|
this.folderInfoSource.next(this.currentFolder);
|
||||||
}),
|
}),
|
||||||
switchMap(() => this.getRuleSets(nodeId)),
|
switchMap(() => combineLatest(this.getMainRuleSet(nodeId), this.getInheritedRuleSets(nodeId))),
|
||||||
finalize(() => this.isLoadingSource.next(false))
|
finalize(() => this.isLoadingSource.next(false))
|
||||||
)
|
)
|
||||||
.subscribe((ruleSets: RuleSet[]) => {
|
.subscribe(([mainRuleSet, inheritedRuleSets]) => {
|
||||||
this.ruleSets = ruleSets;
|
this.mainRuleSet = mainRuleSet;
|
||||||
this.ruleSetListingSource.next(this.ruleSets);
|
this.inheritedRuleSets = inheritedRuleSets;
|
||||||
|
this.mainRuleSetSource.next(mainRuleSet);
|
||||||
|
this.inheritedRuleSetsSource.next(inheritedRuleSets);
|
||||||
this.hasMoreRuleSetsSource.next(this.hasMoreRuleSets);
|
this.hasMoreRuleSetsSource.next(this.hasMoreRuleSets);
|
||||||
this.folderRulesService.selectRule(this.getOwnedOrLinkedRuleSet()?.rules[0] ?? ruleSets[0]?.rules[0]);
|
this.folderRulesService.selectRule(mainRuleSet?.rules[0] ?? inheritedRuleSets[0]?.rules[0] ?? null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMoreRuleSets(selectLastRule = false) {
|
loadMoreInheritedRuleSets() {
|
||||||
this.isLoadingSource.next(true);
|
this.isLoadingSource.next(true);
|
||||||
this.getRuleSets(this.currentFolder.id, this.ruleSets.length)
|
this.getInheritedRuleSets(this.currentFolder.id, this.inheritedRuleSets.length)
|
||||||
.pipe(finalize(() => this.isLoadingSource.next(false)))
|
.pipe(finalize(() => this.isLoadingSource.next(false)))
|
||||||
.subscribe((ruleSets) => {
|
.subscribe((ruleSets) => {
|
||||||
this.ruleSets.push(...ruleSets);
|
this.inheritedRuleSets.push(...ruleSets);
|
||||||
this.ruleSetListingSource.next(this.ruleSets);
|
this.inheritedRuleSetsSource.next(this.inheritedRuleSets);
|
||||||
this.hasMoreRuleSetsSource.next(this.hasMoreRuleSets);
|
this.hasMoreRuleSetsSource.next(this.hasMoreRuleSets);
|
||||||
if (selectLastRule) {
|
|
||||||
const ownedRuleSet = this.getOwnedOrLinkedRuleSet();
|
|
||||||
this.folderRulesService.selectRule(ownedRuleSet?.rules[ownedRuleSet.rules.length - 1]);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +179,9 @@ export class FolderRuleSetsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private formatRuleSet(entry: any): Observable<RuleSet> {
|
private formatRuleSet(entry: any): Observable<RuleSet> {
|
||||||
|
if (!entry) {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
return combineLatest(
|
return combineLatest(
|
||||||
this.currentFolder?.id === entry.owningFolder ? of(this.currentFolder) : this.getNodeInfo(entry.owningFolder || ''),
|
this.currentFolder?.id === entry.owningFolder ? of(this.currentFolder) : this.getNodeInfo(entry.owningFolder || ''),
|
||||||
this.folderRulesService.getRules(entry.owningFolder || '', entry.id)
|
this.folderRulesService.getRules(entry.owningFolder || '', entry.id)
|
||||||
@ -156,15 +198,38 @@ export class FolderRuleSetsService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRuleSetFromRuleId(ruleId: string): RuleSet {
|
removeRuleFromMainRuleSet(ruleId: string) {
|
||||||
return this.ruleSets.find((ruleSet: RuleSet) => ruleSet.rules.findIndex((r: Rule) => r.id === ruleId) > -1) ?? null;
|
if (this.mainRuleSet) {
|
||||||
|
const index = this.mainRuleSet.rules.findIndex((rule: Rule) => rule.id === ruleId);
|
||||||
|
if (index > -1) {
|
||||||
|
if (this.mainRuleSet.rules.length > 1) {
|
||||||
|
this.mainRuleSet.rules.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
this.mainRuleSet = null;
|
||||||
|
this.mainRuleSetSource.next(this.mainRuleSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getOwnedOrLinkedRuleSet(): RuleSet {
|
addOrUpdateRuleInMainRuleSet(newRule: Rule) {
|
||||||
return (
|
if (this.mainRuleSet) {
|
||||||
this.ruleSets.find(
|
const index = this.mainRuleSet.rules.findIndex((rule: Rule) => rule.id === newRule.id);
|
||||||
(ruleSet: RuleSet) => ruleSet.owningFolder.id === this.currentFolder.id || ruleSet.linkedToBy.indexOf(this.currentFolder.id) > -1
|
if (index > -1) {
|
||||||
) ?? null
|
this.mainRuleSet.rules.splice(index, 1, newRule);
|
||||||
);
|
} else {
|
||||||
|
this.mainRuleSet.rules.push(newRule);
|
||||||
|
}
|
||||||
|
this.folderRulesService.selectRule(newRule);
|
||||||
|
} else {
|
||||||
|
this.getMainRuleSet(this.currentFolder.id).subscribe((mainRuleSet: RuleSet) => {
|
||||||
|
this.mainRuleSet = mainRuleSet;
|
||||||
|
this.mainRuleSetSource.next(mainRuleSet);
|
||||||
|
if (mainRuleSet) {
|
||||||
|
const ruleToSelect = mainRuleSet.rules.find((rule: Rule) => rule.id === newRule.id);
|
||||||
|
this.folderRulesService.selectRule(ruleToSelect);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,8 @@ describe('FolderRulesService', () => {
|
|||||||
const nodeId = owningFolderIdMock;
|
const nodeId = owningFolderIdMock;
|
||||||
const ruleSetId = 'rule-set-id';
|
const ruleSetId = 'rule-set-id';
|
||||||
const mockedRule = ruleMock('rule-mock');
|
const mockedRule = ruleMock('rule-mock');
|
||||||
|
const { id, ...mockedRuleWithoutId } = mockedRule;
|
||||||
|
const mockedRuleEntry = { entry: mockedRule };
|
||||||
const ruleId = mockedRule.id;
|
const ruleId = mockedRule.id;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -120,14 +122,18 @@ describe('FolderRulesService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should send correct POST request and return created rule', async () => {
|
it('should send correct POST request and return created rule', async () => {
|
||||||
callApiSpy.withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'POST', mockedRule).and.returnValue(Promise.resolve(mockedRule));
|
callApiSpy
|
||||||
const result = await folderRulesService.createRule(nodeId, mockedRule, ruleSetId);
|
.withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'POST', mockedRuleWithoutId)
|
||||||
|
.and.returnValue(Promise.resolve(mockedRuleEntry));
|
||||||
|
const result = await folderRulesService.createRule(nodeId, mockedRuleWithoutId, ruleSetId);
|
||||||
|
|
||||||
expect(result).toEqual(mockedRule);
|
expect(result).toEqual(mockedRule);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should send correct PUT request to update rule and return it', async () => {
|
it('should send correct PUT request to update rule and return it', async () => {
|
||||||
callApiSpy.withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'PUT', mockedRule).and.returnValue(Promise.resolve(mockedRule));
|
callApiSpy
|
||||||
|
.withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'PUT', mockedRule)
|
||||||
|
.and.returnValue(Promise.resolve(mockedRuleEntry));
|
||||||
|
|
||||||
const result = await folderRulesService.updateRule(nodeId, ruleId, mockedRule, ruleSetId);
|
const result = await folderRulesService.updateRule(nodeId, ruleId, mockedRule, ruleSetId);
|
||||||
expect(result).toEqual(mockedRule);
|
expect(result).toEqual(mockedRule);
|
||||||
|
@ -126,23 +126,19 @@ export class FolderRulesService {
|
|||||||
ruleSet.hasMoreRules = res.hasMoreRules;
|
ruleSet.hasMoreRules = res.hasMoreRules;
|
||||||
ruleSet.rules.splice(skipCount);
|
ruleSet.rules.splice(skipCount);
|
||||||
ruleSet.rules.push(...res.rules);
|
ruleSet.rules.push(...res.rules);
|
||||||
if (selectRule === 'first') {
|
this.selectRuleInRuleSet(ruleSet, selectRule);
|
||||||
this.selectRule(ruleSet.rules[0]);
|
|
||||||
} else if (selectRule === 'last') {
|
|
||||||
this.selectRule(ruleSet.rules[ruleSet.rules.length - 1]);
|
|
||||||
} else if (selectRule) {
|
|
||||||
this.selectRule(selectRule);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createRule(nodeId: string, rule: Partial<Rule>, ruleSetId: string): Promise<unknown> {
|
async createRule(nodeId: string, rule: Partial<Rule>, ruleSetId: string = '-default-'): Promise<Rule> {
|
||||||
return this.callApi(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'POST', { ...rule });
|
const response = await this.callApi(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'POST', { ...rule });
|
||||||
|
return this.formatRule(response.entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRule(nodeId: string, ruleId: string, rule: Rule, ruleSetId: string): Promise<unknown> {
|
async updateRule(nodeId: string, ruleId: string, rule: Rule, ruleSetId: string = '-default-'): Promise<Rule> {
|
||||||
return this.callApi(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'PUT', { ...rule });
|
const response = await this.callApi(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'PUT', { ...rule });
|
||||||
|
return this.formatRule(response.entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteRule(nodeId: string, ruleId: string, ruleSetId: string = '-default-') {
|
deleteRule(nodeId: string, ruleId: string, ruleSetId: string = '-default-') {
|
||||||
@ -207,4 +203,14 @@ export class FolderRulesService {
|
|||||||
selectRule(rule: Rule) {
|
selectRule(rule: Rule) {
|
||||||
this.selectedRuleSource.next(rule);
|
this.selectedRuleSource.next(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectRuleInRuleSet(ruleSet: RuleSet, selectRule: 'first' | 'last' | Rule = null) {
|
||||||
|
if (selectRule === 'first') {
|
||||||
|
this.selectRule(ruleSet.rules[0]);
|
||||||
|
} else if (selectRule === 'last') {
|
||||||
|
this.selectRule(ruleSet.rules[ruleSet.rules.length - 1]);
|
||||||
|
} else if (selectRule) {
|
||||||
|
this.selectRule(selectRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user