Separates the menu for multi-select of motions

- Adds a new component for the list.
- Adds the component to amendment-list and motion-list.
This commit is contained in:
GabrielMeyer 2019-09-19 11:58:06 +02:00
parent 4a8362deaf
commit a56d850f51
11 changed files with 271 additions and 111 deletions

View File

@ -136,5 +136,11 @@
<mat-icon>clear</mat-icon> <mat-icon>clear</mat-icon>
<span translate>Deselect all</span> <span translate>Deselect all</span>
</button> </button>
<ng-container *osPerms="'motions.can_manage'; or: 'motions.can_manage_metadata'">
<mat-divider></mat-divider>
<os-motion-multiselect-actions [selectedMotions]="selectedRows" (action)="multiselectWrapper($event)">
</os-motion-multiselect-actions>
</ng-container>
</div> </div>
</mat-menu> </mat-menu>

View File

@ -3,6 +3,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module'; import { E2EImportsModule } from 'e2e-imports.module';
import { AmendmentListComponent } from './amendment-list.component'; import { AmendmentListComponent } from './amendment-list.component';
import { MotionMultiselectActionsComponent } from '../shared-motion/motion-multiselect-actions/motion-multiselect-actions.component';
describe('AmendmentListComponent', () => { describe('AmendmentListComponent', () => {
let component: AmendmentListComponent; let component: AmendmentListComponent;
@ -11,7 +12,7 @@ describe('AmendmentListComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [E2EImportsModule], imports: [E2EImportsModule],
declarations: [AmendmentListComponent] declarations: [AmendmentListComponent, MotionMultiselectActionsComponent]
}).compileComponents(); }).compileComponents();
})); }));

View File

@ -13,11 +13,13 @@ import { AmendmentSortListService } from '../../services/amendment-sort-list.ser
import { StorageService } from 'app/core/core-services/storage.service'; import { StorageService } from 'app/core/core-services/storage.service';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service'; import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
import { OverlayService } from 'app/core/ui-services/overlay.service';
import { ItemVisibilityChoices } from 'app/shared/models/agenda/item'; import { ItemVisibilityChoices } from 'app/shared/models/agenda/item';
import { largeDialogSettings } from 'app/shared/utils/dialog-settings'; import { largeDialogSettings } from 'app/shared/utils/dialog-settings';
import { BaseListViewComponent } from 'app/site/base/base-list-view'; import { BaseListViewComponent } from 'app/site/base/base-list-view';
import { MotionExportDialogComponent } from '../shared-motion/motion-export-dialog/motion-export-dialog.component'; import { MotionExportDialogComponent } from '../shared-motion/motion-export-dialog/motion-export-dialog.component';
import { MotionExportInfo, MotionExportService } from '../../services/motion-export.service'; import { MotionExportInfo, MotionExportService } from '../../services/motion-export.service';
import { MotionMultiselectService } from '../../services/motion-multiselect.service';
import { MotionPdfExportService } from '../../services/motion-pdf-export.service'; import { MotionPdfExportService } from '../../services/motion-pdf-export.service';
import { MotionSortListService } from '../../services/motion-sort-list.service'; import { MotionSortListService } from '../../services/motion-sort-list.service';
import { ViewMotion } from '../../models/view-motion'; import { ViewMotion } from '../../models/view-motion';
@ -92,12 +94,14 @@ export class AmendmentListComponent extends BaseListViewComponent<ViewMotion> im
private route: ActivatedRoute, private route: ActivatedRoute,
public motionRepo: MotionRepositoryService, public motionRepo: MotionRepositoryService,
public motionSortService: MotionSortListService, public motionSortService: MotionSortListService,
public motionMultiSelectService: MotionMultiselectService,
public amendmentSortService: AmendmentSortListService, public amendmentSortService: AmendmentSortListService,
public amendmentFilterService: AmendmentFilterListService, public amendmentFilterService: AmendmentFilterListService,
private dialog: MatDialog, private dialog: MatDialog,
private motionExport: MotionExportService, private motionExport: MotionExportService,
private linenumberingService: LinenumberingService, private linenumberingService: LinenumberingService,
private pdfExport: MotionPdfExportService private pdfExport: MotionPdfExportService,
private overlayService: OverlayService
) { ) {
super(titleService, translate, matSnackBar, storage); super(titleService, translate, matSnackBar, storage);
super.setTitle('Amendments'); super.setTitle('Amendments');
@ -155,6 +159,15 @@ export class AmendmentListComponent extends BaseListViewComponent<ViewMotion> im
); );
} }
/**
* Function to await the promises. Afterwards it will hide the spinner.
*
* @param action The promise to await.
*/
public async multiselectWrapper(action: Promise<void>): Promise<void> {
action.then(() => this.overlayService.hideSpinner(), this.raiseError);
}
/** /**
* Export the given motion ist as special PDF * Export the given motion ist as special PDF
*/ */

View File

@ -325,107 +325,27 @@
<mat-icon>clear</mat-icon> <mat-icon>clear</mat-icon>
<span translate>Deselect all</span> <span translate>Deselect all</span>
</button> </button>
<div *ngIf="perms.isAllowed('change_metadata')"> <ng-container *ngIf="perms.isAllowed('change_metadata')">
<mat-divider></mat-divider> <mat-divider></mat-divider>
<button <os-motion-multiselect-actions [selectedMotions]="selectedRows" (action)="multiselectWrapper($event)">
mat-menu-item <button
[disabled]="!selectedRows.length" mat-menu-item
(click)="multiselectWrapper(multiselectService.bulkSetFavorite(selectedRows))" [disabled]="!selectedRows.length"
> (click)="multiselectWrapper(multiselectService.moveToItem(selectedRows))"
<mat-icon>star</mat-icon> >
<span translate>Set favorite</span> <mat-icon>sort</mat-icon>
</button> <span translate>Move to agenda item</span>
<button </button>
mat-menu-item <button
[disabled]="!selectedRows.length" mat-menu-item
(click)="multiselectWrapper(multiselectService.setStateOfMultiple(selectedRows))" [disabled]="!selectedRows.length"
> (click)="multiselectWrapper(multiselectService.bulkMoveItems(selectedRows))"
<mat-icon>label</mat-icon> >
<span translate>Set status</span> <mat-icon>format_indent_increase</mat-icon>
</button> <span translate>Move in call list</span>
<button </button>
*ngIf="recommendationEnabled" </os-motion-multiselect-actions>
[disabled]="!selectedRows.length" </ng-container>
mat-menu-item
(click)="multiselectWrapper(multiselectService.setRecommendation(selectedRows))"
>
<mat-icon>report</mat-icon>
<!-- TODO: better icon -->
<span translate>Set recommendation</span>
</button>
<button
mat-menu-item
[disabled]="!selectedRows.length"
*ngIf="categories.length"
(click)="multiselectWrapper(multiselectService.setCategory(selectedRows))"
>
<mat-icon>category</mat-icon>
<!-- TODO: icon -->
<span translate>Set category</span>
</button>
<button
mat-menu-item
*ngIf="motionBlocks.length"
[disabled]="!selectedRows.length"
(click)="multiselectWrapper(multiselectService.setMotionBlock(selectedRows))"
>
<mat-icon>widgets</mat-icon>
<!-- TODO: icon -->
<span translate>Set motion block</span>
</button>
<button
mat-menu-item
[disabled]="!selectedRows.length"
(click)="multiselectWrapper(multiselectService.changeSubmitters(selectedRows))"
>
<mat-icon>person_add</mat-icon>
<!-- TODO: icon -->
<span translate>Add/remove submitters</span>
</button>
<button
mat-menu-item
*ngIf="tags.length"
[disabled]="!selectedRows.length"
(click)="multiselectWrapper(multiselectService.changeTags(selectedRows))"
>
<mat-icon>bookmarks</mat-icon>
<!-- TODO: icon -->
<span translate>Add/remove tags</span>
</button>
<button
mat-menu-item
[disabled]="!selectedRows.length"
(click)="multiselectWrapper(multiselectService.moveToItem(selectedRows))"
>
<mat-icon>sort</mat-icon>
<span translate>Move to agenda item</span>
</button>
<button
mat-menu-item
[disabled]="!selectedRows.length"
(click)="multiselectWrapper(multiselectService.bulkMoveItems(selectedRows))"
>
<mat-icon>format_indent_increase</mat-icon>
<span translate>Move in call list</span>
</button>
</div>
<div *ngIf="perms.isAllowed('manage')">
<button mat-menu-item [disabled]="!selectedRows.length" (click)="openExportDialog()">
<mat-icon>archive</mat-icon>
<span translate>Export selected motions</span>
</button>
<mat-divider></mat-divider>
<button
mat-menu-item
class="red-warning-text"
[disabled]="!selectedRows.length"
(click)="multiselectWrapper(multiselectService.delete(selectedRows)); toggleMultiSelect()"
>
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
</div>
</div> </div>
</mat-menu> </mat-menu>

View File

@ -3,6 +3,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module'; import { E2EImportsModule } from 'e2e-imports.module';
import { MotionListComponent } from './motion-list.component'; import { MotionListComponent } from './motion-list.component';
import { MotionMultiselectActionsComponent } from '../../../shared-motion/motion-multiselect-actions/motion-multiselect-actions.component';
describe('MotionListComponent', () => { describe('MotionListComponent', () => {
let component: MotionListComponent; let component: MotionListComponent;
@ -11,7 +12,7 @@ describe('MotionListComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [E2EImportsModule], imports: [E2EImportsModule],
declarations: [MotionListComponent] declarations: [MotionListComponent, MotionMultiselectActionsComponent]
}).compileComponents(); }).compileComponents();
})); }));

View File

@ -362,13 +362,7 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
* @param multiselectPromise The promise returned by multiselect actions. * @param multiselectPromise The promise returned by multiselect actions.
*/ */
public async multiselectWrapper(multiselectPromise: Promise<void>): Promise<void> { public async multiselectWrapper(multiselectPromise: Promise<void>): Promise<void> {
try { multiselectPromise.then(() => this.overlayService.hideSpinner(), this.raiseError);
await multiselectPromise;
} catch (e) {
this.raiseError(e);
} finally {
this.overlayService.hideSpinner();
}
} }
/** /**

View File

@ -0,0 +1,85 @@
<button
mat-menu-item
[disabled]="!selectedMotions.length"
(click)="action.emit(multiselectService.bulkSetFavorite(selectedMotions))"
>
<mat-icon>star</mat-icon>
<span translate>Set favorite</span>
</button>
<button
mat-menu-item
[disabled]="!selectedMotions.length"
(click)="action.emit(multiselectService.setStateOfMultiple(selectedMotions))"
>
<mat-icon>label</mat-icon>
<span translate>Set status</span>
</button>
<button
*ngIf="recommendationEnabled"
[disabled]="!selectedMotions.length"
mat-menu-item
(click)="action.emit(multiselectService.setRecommendation(selectedMotions))"
>
<mat-icon>report</mat-icon>
<!-- TODO: better icon -->
<span translate>Set recommendation</span>
</button>
<button
mat-menu-item
[disabled]="!selectedMotions.length"
*ngIf="categories.length"
(click)="action.emit(multiselectService.setCategory(selectedMotions))"
>
<mat-icon>category</mat-icon>
<!-- TODO: icon -->
<span translate>Set category</span>
</button>
<button
mat-menu-item
*ngIf="motionBlocks.length"
[disabled]="!selectedMotions.length"
(click)="action.emit(multiselectService.setMotionBlock(selectedMotions))"
>
<mat-icon>widgets</mat-icon>
<!-- TODO: icon -->
<span translate>Set motion block</span>
</button>
<button
mat-menu-item
[disabled]="!selectedMotions.length"
(click)="action.emit(multiselectService.changeSubmitters(selectedMotions))"
>
<mat-icon>person_add</mat-icon>
<!-- TODO: icon -->
<span translate>Add/remove submitters</span>
</button>
<button
mat-menu-item
*ngIf="tags.length"
[disabled]="!selectedMotions.length"
(click)="action.emit(multiselectService.changeTags(selectedMotions))"
>
<mat-icon>bookmarks</mat-icon>
<!-- TODO: icon -->
<span translate>Add/remove tags</span>
</button>
<ng-content></ng-content>
<ng-container *osPerms="'motions.can_manage'">
<button mat-menu-item [disabled]="!selectedMotions.length" (click)="openExportDialog()">
<mat-icon>archive</mat-icon>
<span translate>Export selected motions</span>
</button>
<mat-divider></mat-divider>
<button
mat-menu-item
class="red-warning-text"
[disabled]="!selectedMotions.length"
(click)="action.emit(multiselectService.delete(selectedMotions))"
>
<mat-icon>delete</mat-icon>
<span translate>Delete</span>
</button>
</ng-container>

View File

@ -0,0 +1,27 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module';
import { MotionMultiselectActionsComponent } from './motion-multiselect-actions.component';
describe('MotionMultiselectActionsComponent', () => {
let component: MotionMultiselectActionsComponent;
let fixture: ComponentFixture<MotionMultiselectActionsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule],
declarations: [MotionMultiselectActionsComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MotionMultiselectActionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,111 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { MatDialog, MatSnackBar } from '@angular/material';
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
import { ConfigService } from 'app/core/ui-services/config.service';
import { largeDialogSettings } from 'app/shared/utils/dialog-settings';
import { BaseViewComponent } from 'app/site/base/base-view';
import { ViewCategory } from 'app/site/motions/models/view-category';
import { ViewMotion } from 'app/site/motions/models/view-motion';
import { ViewMotionBlock } from 'app/site/motions/models/view-motion-block';
import { MotionExportInfo, MotionExportService } from 'app/site/motions/services/motion-export.service';
import { MotionMultiselectService } from 'app/site/motions/services/motion-multiselect.service';
import { ViewTag } from 'app/site/tags/models/view-tag';
import { MotionExportDialogComponent } from '../motion-export-dialog/motion-export-dialog.component';
@Component({
selector: 'os-motion-multiselect-actions',
templateUrl: './motion-multiselect-actions.component.html',
styleUrls: ['./motion-multiselect-actions.component.scss']
})
export class MotionMultiselectActionsComponent extends BaseViewComponent implements OnInit {
/**
* The list of the selected motions.
*/
@Input()
public selectedMotions: ViewMotion[] = [];
/**
* An EventEmitter to send the selected actions.
*/
@Output()
public action = new EventEmitter<Promise<void>>();
/**
* Boolean, if the recommendation is enabled.
*/
public recommendationEnabled = false;
/**
* The list of all categories.
*/
public categories: ViewCategory[] = [];
/**
* The list of all tags.
*/
public tags: ViewTag[] = [];
/**
* The list of all motion-blocks.
*/
public motionBlocks: ViewMotionBlock[] = [];
/**
* The default constructor.
*
* @param multiselectService
* @param categoryRepo
* @param motionBlockRepo
* @param tagRepo
* @param configService
*/
public constructor(
title: Title,
protected translate: TranslateService,
matSnackbar: MatSnackBar,
public multiselectService: MotionMultiselectService,
private categoryRepo: CategoryRepositoryService,
private motionBlockRepo: MotionBlockRepositoryService,
private tagRepo: TagRepositoryService,
private configService: ConfigService,
private dialog: MatDialog,
private motionExport: MotionExportService
) {
super(title, translate, matSnackbar);
}
/**
* OnInit-method.
*
* Subscribe to all view-model-lists.
*/
public ngOnInit(): void {
this.subscriptions.push(
this.categoryRepo.getViewModelListObservable().subscribe(categories => (this.categories = categories)),
this.motionBlockRepo.getViewModelListObservable().subscribe(blocks => (this.motionBlocks = blocks)),
this.tagRepo.getViewModelListObservable().subscribe(tags => (this.tags = tags)),
this.configService.get<string>('motions_recommendations_by').subscribe(recommender => {
this.recommendationEnabled = !!recommender;
})
);
}
/**
* Opens the dialog to choose options for exporting selected motions.
*/
public openExportDialog(): void {
const exportDialogRef = this.dialog.open(MotionExportDialogComponent, largeDialogSettings);
exportDialogRef
.afterClosed()
.subscribe((exportInfo: MotionExportInfo) =>
this.motionExport.evaluateExportRequest(exportInfo, this.selectedMotions)
);
}
}

View File

@ -3,10 +3,12 @@ import { NgModule } from '@angular/core';
import { SharedModule } from 'app/shared/shared.module'; import { SharedModule } from 'app/shared/shared.module';
import { MotionExportDialogComponent } from './motion-export-dialog/motion-export-dialog.component'; import { MotionExportDialogComponent } from './motion-export-dialog/motion-export-dialog.component';
import { MotionMultiselectActionsComponent } from './motion-multiselect-actions/motion-multiselect-actions.component';
@NgModule({ @NgModule({
imports: [CommonModule, SharedModule], imports: [CommonModule, SharedModule],
declarations: [MotionExportDialogComponent], exports: [MotionMultiselectActionsComponent],
declarations: [MotionExportDialogComponent, MotionMultiselectActionsComponent],
entryComponents: [MotionExportDialogComponent] entryComponents: [MotionExportDialogComponent]
}) })
export class SharedMotionModule {} export class SharedMotionModule {}