diff --git a/projects/aca-folder-rules/assets/i18n/en.json b/projects/aca-folder-rules/assets/i18n/en.json index 0b2dd6bd9..0d6a12734 100644 --- a/projects/aca-folder-rules/assets/i18n/en.json +++ b/projects/aca-folder-rules/assets/i18n/en.json @@ -83,6 +83,12 @@ "TITLE": "The list is empty", "SUBTITLE": "There are no rules defined for this folder yet." } + }, + "CONFIRMATION_DIALOG": { + "DELETE_RULE": { + "TITLE": "Delete rule", + "MESSAGE": "Are you sure you want to delete this rule?" + } } } } diff --git a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.html b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.html index 4e18f7acc..4bb3c3bdd 100644 --- a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.html +++ b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.html @@ -32,7 +32,18 @@
- +
+
+ {{ selectedRule.name }} +
+ +
+
+

{{ selectedRule.description }}

+
+
diff --git a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.scss b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.scss index d2650773d..74c6e3cd3 100644 --- a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.scss +++ b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.scss @@ -21,6 +21,34 @@ padding: 32px; overflow: scroll; + &__preview { + padding: 0 20px; + + &__toolbar { + display: flex; + align-items: center; + justify-content: space-between; + + span { + font-style: normal; + font-weight: 700; + font-size: 14px; + line-height: 20px; + } + + &__buttons { + display: inline-block; + } + } + + p { + font-style: normal; + font-weight: 400; + font-size: 12px; + line-height: 16px; + } + } + &__rule-details { overflow-x: scroll; } diff --git a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.spec.ts b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.spec.ts index 28412bbd3..02aa2e1fa 100644 --- a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.spec.ts +++ b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.spec.ts @@ -33,6 +33,7 @@ import { of } from 'rxjs'; import { dummyRules } from '../mock/rules.mock'; import { By } from '@angular/platform-browser'; import { dummyNodeInfo } from '../mock/node.mock'; +import { MatDialog } from '@angular/material/dialog'; describe('ManageRulesSmartComponent', () => { let fixture: ComponentFixture; @@ -42,7 +43,7 @@ describe('ManageRulesSmartComponent', () => { beforeEach( waitForAsync(() => { - const folderRulesServiceSpy = jasmine.createSpyObj('FolderRulesService', ['loadRules']); + const folderRulesServiceSpy = jasmine.createSpyObj('FolderRulesService', ['loadRules', 'deleteRule']); TestBed.configureTestingModule({ imports: [CoreTestingModule, AcaFolderRulesModule], providers: [ @@ -61,6 +62,7 @@ describe('ManageRulesSmartComponent', () => { ); it('should display aca-rules-list and aca-rule-details', () => { + folderRulesService.deletedRuleId$ = of(null); folderRulesService.folderInfo$ = of(dummyNodeInfo); folderRulesService.rulesListing$ = of(dummyRules); folderRulesService.loading$ = of(false); @@ -73,15 +75,18 @@ describe('ManageRulesSmartComponent', () => { const rules = debugElement.queryAll(By.css('.aca-rule')); const ruleDetails = debugElement.queryAll(By.css('aca-rule-details')); + const deleteRuleBtn = debugElement.query(By.css('#delete-rule-btn')); - expect(rules.length).toBe(2, 'Unexpected number of aca-rule'); + expect(rules.length).toBe(2, 'unexpected number of aca-rule'); expect(ruleDetails.length).toBeTruthy('aca-rule-details was not rendered'); + expect(deleteRuleBtn).toBeTruthy('no delete rule button'); }); it('should only show adf-empty-content if provided node has no rules defined yet', () => { folderRulesService.folderInfo$ = of(dummyNodeInfo); folderRulesService.rulesListing$ = of([]); folderRulesService.loading$ = of(false); + folderRulesService.deletedRuleId$ = of(null); fixture.detectChanges(); @@ -98,6 +103,7 @@ describe('ManageRulesSmartComponent', () => { it('should only show aca-generic-error if the non-existing node was provided', () => { folderRulesService.folderInfo$ = of(null); + folderRulesService.deletedRuleId$ = of(null); folderRulesService.rulesListing$ = of([]); folderRulesService.loading$ = of(false); @@ -116,6 +122,7 @@ describe('ManageRulesSmartComponent', () => { it('should only show progress bar while loading', () => { folderRulesService.folderInfo$ = of(null); + folderRulesService.deletedRuleId$ = of(null); folderRulesService.rulesListing$ = of([]); folderRulesService.loading$ = of(true); @@ -131,4 +138,57 @@ describe('ManageRulesSmartComponent', () => { expect(rules).toBeFalsy(); expect(ruleDetails).toBeFalsy(); }); + + it('should call deleteRule() if confirmation dialog returns true', () => { + const dialog = TestBed.inject(MatDialog); + folderRulesService.deletedRuleId$ = of(null); + folderRulesService.folderInfo$ = of(dummyNodeInfo); + folderRulesService.rulesListing$ = of(dummyRules); + folderRulesService.loading$ = of(false); + + spyOn(component, 'onRuleDelete').and.callThrough(); + + const dialogResult: any = { + afterClosed: () => + of(true).subscribe((res) => { + if (res === true) { + folderRulesService.deleteRule(component.nodeId, component.selectedRule.id); + } + }) + }; + spyOn(dialog, 'open').and.returnValue(dialogResult); + + fixture.detectChanges(); + expect(component).toBeTruthy('expected component'); + + const rules = debugElement.queryAll(By.css('.aca-rule')); + 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(component.selectedRule.id); + + expect(component.onRuleDelete).toHaveBeenCalled(); + expect(dialog.open).toHaveBeenCalled(); + expect(folderRulesService.deleteRule).toHaveBeenCalled(); + expect(folderRulesService.loadRules).toHaveBeenCalledTimes(1); + expect(rules).toBeTruthy('expected rules'); + expect(ruleDetails).toBeTruthy('expected ruleDetails'); + expect(deleteRuleBtn).toBeTruthy(); + }); + + it('should run loadRules() when deletedRuleId$ emits new value', () => { + folderRulesService.deletedRuleId$ = of('new-value'); + folderRulesService.folderInfo$ = of(dummyNodeInfo); + folderRulesService.rulesListing$ = of(dummyRules); + folderRulesService.loading$ = of(false); + + fixture.detectChanges(); + + expect(component).toBeTruthy(); + + expect(folderRulesService.loadRules).toHaveBeenCalledTimes(2); + }); }); diff --git a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.ts b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.ts index 30e34b520..9c624ae41 100644 --- a/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.ts +++ b/projects/aca-folder-rules/src/lib/manage-rules/manage-rules.smart-component.ts @@ -23,16 +23,17 @@ * along with Alfresco. If not, see . */ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; import { Location } from '@angular/common'; import { FolderRulesService } from '../services/folder-rules.service'; -import { Observable } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { Rule } from '../model/rule.model'; import { ActivatedRoute } from '@angular/router'; import { NodeInfo } from '@alfresco/aca-shared/store'; import { tap } from 'rxjs/operators'; import { EditRuleDialogSmartComponent } from '../rule-details/edit-rule-dialog.smart-component'; import { MatDialog } from '@angular/material/dialog'; +import { ConfirmDialogComponent } from '@alfresco/adf-content-services'; @Component({ selector: 'aca-manage-rules', @@ -41,12 +42,13 @@ import { MatDialog } from '@angular/material/dialog'; encapsulation: ViewEncapsulation.None, host: { class: 'aca-manage-rules' } }) -export class ManageRulesSmartComponent implements OnInit { +export class ManageRulesSmartComponent implements OnInit, OnDestroy { rules$: Observable; isLoading$: Observable; folderInfo$: Observable; selectedRule: Rule = null; nodeId: string = null; + deletedRuleSubscription$: Subscription; constructor( private location: Location, @@ -63,6 +65,11 @@ export class ManageRulesSmartComponent implements OnInit { } }) ); + this.deletedRuleSubscription$ = this.folderRulesService.deletedRuleId$.subscribe((deletedRuleId) => { + if (deletedRuleId) { + this.folderRulesService.loadRules(this.nodeId); + } + }); this.isLoading$ = this.folderRulesService.loading$; this.folderInfo$ = this.folderRulesService.folderInfo$; this.route.params.subscribe((params) => { @@ -73,6 +80,10 @@ export class ManageRulesSmartComponent implements OnInit { }); } + ngOnDestroy(): void { + this.deletedRuleSubscription$.unsubscribe(); + } + goBack(): void { this.location.back(); } @@ -87,4 +98,21 @@ export class ManageRulesSmartComponent implements OnInit { panelClass: 'aca-edit-rule-dialog-container' }); } + + onRuleDelete(): void { + 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, this.selectedRule.id); + } + }); + } } diff --git a/projects/aca-folder-rules/src/lib/rule-details/rule-details.ui-component.html b/projects/aca-folder-rules/src/lib/rule-details/rule-details.ui-component.html index ddb4ae129..533d49a4d 100644 --- a/projects/aca-folder-rules/src/lib/rule-details/rule-details.ui-component.html +++ b/projects/aca-folder-rules/src/lib/rule-details/rule-details.ui-component.html @@ -1,29 +1,32 @@
-
- -
- - - {{ getErrorMessage(name) | translate }} - -
-
-
- -
- + +
+ +
+ + + {{ getErrorMessage(name) | translate }} + +
+
+ +
+ +
+ - + +
-
+
diff --git a/projects/aca-folder-rules/src/lib/rule-details/rule-details.ui-component.ts b/projects/aca-folder-rules/src/lib/rule-details/rule-details.ui-component.ts index 053d85b3e..709240457 100644 --- a/projects/aca-folder-rules/src/lib/rule-details/rule-details.ui-component.ts +++ b/projects/aca-folder-rules/src/lib/rule-details/rule-details.ui-component.ts @@ -72,6 +72,8 @@ export class RuleDetailsUiComponent implements OnInit, OnDestroy { this._initialValue = newValue; } } + @Input() + preview: boolean; @Output() formValidationChanged = new EventEmitter(); diff --git a/projects/aca-folder-rules/src/lib/services/folder-rules.service.spec.ts b/projects/aca-folder-rules/src/lib/services/folder-rules.service.spec.ts index 2c7f937f8..1626a7527 100644 --- a/projects/aca-folder-rules/src/lib/services/folder-rules.service.spec.ts +++ b/projects/aca-folder-rules/src/lib/services/folder-rules.service.spec.ts @@ -39,25 +39,33 @@ describe('FolderRulesService', () => { let contentApi: ContentApiService; let rulesPromise: Promise[]>; let folderInfoPromise: Promise; + let deletedRulePromise: Promise; let rules: Partial[]; let folderInfo: NodeInfo; + let deletedRule: string; let apiCallSpy; let getNodeSpy; const nodeId = '********-fake-node-****-********'; + const ruleId = '********-fake-rule-****-********'; const ruleSetId = '-default-'; + const params = [{}, {}, {}, {}, {}, ['application/json'], ['application/json']]; + + beforeEach(async () => { + TestBed.configureTestingModule({ + imports: [CoreTestingModule], + providers: [FolderRulesService, ContentApiService] + }); + folderRulesService = TestBed.inject(FolderRulesService); + }); describe('loadRules', () => { beforeEach(async () => { - TestBed.configureTestingModule({ - imports: [CoreTestingModule], - providers: [FolderRulesService, ContentApiService] - }); - - folderRulesService = TestBed.inject(FolderRulesService); contentApi = TestBed.inject(ContentApiService); - apiCallSpy = spyOn(folderRulesService, 'apiCall').and.returnValue(of(dummyResponse) as any); + apiCallSpy = spyOn(folderRulesService, 'apiCall') + .withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'GET', params) + .and.returnValue(of(dummyResponse) as any); getNodeSpy = spyOn(contentApi, 'getNode').and.returnValue(of(dummyGetNodeResponse) as any); rulesPromise = folderRulesService.rulesListing$.pipe(take(2)).toPromise(); @@ -76,7 +84,29 @@ describe('FolderRulesService', () => { expect(rules).toEqual(dummyRules, 'The list of rules is incorrectly formatted'); expect(folderInfo).toEqual(dummyNodeInfo, 'The node info is wrong'); expect(apiCallSpy).toHaveBeenCalledTimes(1); + expect(apiCallSpy).toHaveBeenCalledWith(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'GET', params); expect(getNodeSpy).toHaveBeenCalledTimes(1); }); }); + + describe('deleteRule', () => { + beforeEach(async () => { + apiCallSpy = spyOn(folderRulesService, 'apiCall') + .withArgs(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'DELETE', params) + .and.returnValue(ruleId); + + deletedRulePromise = folderRulesService.deletedRuleId$.pipe(take(2)).toPromise(); + + folderRulesService.deleteRule(nodeId, ruleId, ruleSetId); + + deletedRule = await deletedRulePromise; + }); + + it('should delete a rule and return its id', async () => { + expect(deletedRule).toBeTruthy('rule has not been deleted'); + expect(deletedRule).toBe(ruleId, 'wrong id of deleted rule'); + expect(apiCallSpy).toHaveBeenCalledTimes(1); + expect(apiCallSpy).toHaveBeenCalledWith(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'DELETE', params); + }); + }); }); diff --git a/projects/aca-folder-rules/src/lib/services/folder-rules.service.ts b/projects/aca-folder-rules/src/lib/services/folder-rules.service.ts index 2a2d13fee..262bc5ff8 100644 --- a/projects/aca-folder-rules/src/lib/services/folder-rules.service.ts +++ b/projects/aca-folder-rules/src/lib/services/folder-rules.service.ts @@ -68,6 +68,8 @@ export class FolderRulesService { folderInfo$: Observable = this.folderInfoSource.asObservable(); private loadingSource = new BehaviorSubject(false); loading$ = this.loadingSource.asObservable(); + private deletedRuleIdSource = new BehaviorSubject(null); + deletedRuleId$: Observable = this.deletedRuleIdSource.asObservable(); constructor(private apiService: AlfrescoApiService, private contentApi: ContentApiService) {} @@ -107,6 +109,29 @@ export class FolderRulesService { ); } + deleteRule(nodeId: string, ruleId: string, ruleSetId: string = '-default-'): void { + this.loadingSource.next(true); + from( + this.apiCall(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules/${ruleId}`, 'DELETE', [ + {}, + {}, + {}, + {}, + {}, + ['application/json'], + ['application/json'] + ]) + ).subscribe( + () => { + this.deletedRuleIdSource.next(ruleId); + }, + (error) => { + this.deletedRuleIdSource.next(error); + this.loadingSource.next(false); + } + ); + } + private apiCall(path: string, httpMethod: string, params?: any[]): Promise { return this.apiService.getInstance().contentClient.callApi(path, httpMethod, ...params); }