Merge pull request #4075 from MaximilianKrambach/multiSelect-tweaks
Multiselect dialogs with multiple choice options
This commit is contained in:
commit
256f4e75a3
@ -27,17 +27,32 @@ export class ChoiceService extends OpenSlidesComponent {
|
|||||||
* Opens the dialog. Returns the chosen value after the user accepts.
|
* Opens the dialog. Returns the chosen value after the user accepts.
|
||||||
* @param title The title to display in the dialog
|
* @param title The title to display in the dialog
|
||||||
* @param choices The available choices
|
* @param choices The available choices
|
||||||
|
* @param multiSelect turn on the option to select multiple entries.
|
||||||
|
* The answer.items will then be an array.
|
||||||
|
* @param actions optional strings for buttons replacing the regular confirmation.
|
||||||
|
* The answer.action will reflect the button selected
|
||||||
|
* @param clearChoice A string for an extra, visually slightly separated
|
||||||
|
* choice for 'explicitly set an empty selection'. The answer's action may
|
||||||
|
* have this string's value
|
||||||
* @returns an answer {@link ChoiceAnswer}
|
* @returns an answer {@link ChoiceAnswer}
|
||||||
*/
|
*/
|
||||||
public async open(
|
public async open(
|
||||||
title: string,
|
title: string,
|
||||||
choices: ChoiceDialogOptions,
|
choices: ChoiceDialogOptions,
|
||||||
multiSelect: boolean = false
|
multiSelect: boolean = false,
|
||||||
|
actions?: string[],
|
||||||
|
clearChoice?: string
|
||||||
): Promise<ChoiceAnswer> {
|
): Promise<ChoiceAnswer> {
|
||||||
const dialogRef = this.dialog.open(ChoiceDialogComponent, {
|
const dialogRef = this.dialog.open(ChoiceDialogComponent, {
|
||||||
minWidth: '250px',
|
minWidth: '250px',
|
||||||
maxHeight: '90vh',
|
maxHeight: '90vh',
|
||||||
data: { title: title, choices: choices, multiSelect: multiSelect }
|
data: {
|
||||||
|
title: title,
|
||||||
|
choices: choices,
|
||||||
|
multiSelect: multiSelect,
|
||||||
|
actionButtons: actions,
|
||||||
|
clearChoice: clearChoice
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return dialogRef.afterClosed().toPromise();
|
return dialogRef.afterClosed().toPromise();
|
||||||
}
|
}
|
||||||
|
@ -4,29 +4,41 @@
|
|||||||
<mat-radio-group
|
<mat-radio-group
|
||||||
#radio
|
#radio
|
||||||
name="choice"
|
name="choice"
|
||||||
*ngIf="!data.multiSelect"
|
*ngIf="!data.multiSelect && data.choices"
|
||||||
class="choice-radio-group"
|
class="choice-radio-group"
|
||||||
[(ngModel)]="selectedChoice"
|
[(ngModel)]="selectedChoice"
|
||||||
>
|
>
|
||||||
<mat-radio-button class="choice-button" *ngFor="let choice of data.choices" [value]="choice.id">
|
<mat-radio-button class="choice-button" *ngFor="let choice of data.choices" [value]="choice.id">
|
||||||
{{ getChoiceTitle(choice) | translate }}
|
{{ getChoiceTitle(choice) | translate }}
|
||||||
</mat-radio-button>
|
</mat-radio-button>
|
||||||
|
|
||||||
|
<mat-divider *ngIf="data.clearChoice"></mat-divider>
|
||||||
|
|
||||||
|
<mat-radio-button *ngIf="data.clearChoice" [value]="null">
|
||||||
|
{{ data.clearChoice | translate }}
|
||||||
|
</mat-radio-button>
|
||||||
|
|
||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
|
|
||||||
<mat-list *ngIf="data.multiSelect">
|
<mat-list *ngIf="data.multiSelect && data.choices">
|
||||||
<mat-list-item *ngFor="let choice of data.choices">
|
<mat-list-item *ngFor="let choice of data.choices">
|
||||||
<mat-checkbox [checked]="isChosen(choice)" (change)="toggleChoice(choice)">
|
<mat-checkbox [checked]="isChosen(choice)" (change)="toggleChoice(choice)">
|
||||||
{{ getChoiceTitle(choice) | translate }}
|
{{ getChoiceTitle(choice) | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
</mat-list>
|
</mat-list>
|
||||||
|
|
||||||
<span *ngIf="!data.choices.length" translate>No choices available</span>
|
|
||||||
</div>
|
</div>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button *ngIf="!data.multiSelect || data.choices.length" mat-button (click)="closeDialog(true)">
|
<div *ngIf="data.actionButtons">
|
||||||
<span translate>Ok</span>
|
<button *ngFor="let button of data.actionButtons" mat-button (click)="closeDialog(true, button)">
|
||||||
</button>
|
<span translate>{{ button }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!data.actionButtons">
|
||||||
|
<button *ngIf="!data.multiSelect || data.choices.length" mat-button (click)="closeDialog(true)">
|
||||||
|
<span translate>Ok</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<button mat-button (click)="closeDialog(false)"><span translate>Cancel</span></button>
|
<button mat-button (click)="closeDialog(false)"><span translate>Cancel</span></button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,3 +15,7 @@ mat-radio-group {
|
|||||||
.scrollmenu-outer {
|
.scrollmenu-outer {
|
||||||
max-height: inherit;
|
max-height: inherit;
|
||||||
}
|
}
|
||||||
|
mat-divider {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
@ -29,19 +29,33 @@ interface ChoiceDialogData {
|
|||||||
choices: ChoiceDialogOptions;
|
choices: ChoiceDialogOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select, if this should be a multiselect choice
|
* Select if this should be a multiselect choice
|
||||||
*/
|
*/
|
||||||
multiSelect: boolean;
|
multiSelect: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional action buttons which will add their value to the
|
||||||
|
* {@link closeDialog} feedback if chosen
|
||||||
|
*/
|
||||||
|
actionButtons?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional string for 'explicitly select none of the options'. Only
|
||||||
|
* displayed in the single-select variation
|
||||||
|
*/
|
||||||
|
clearChoice?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* undefined is returned, if the dialog is closed. If a choice is submitted,
|
* undefined is returned, if the dialog is closed. If a choice is submitted,
|
||||||
* it might be a number oder an array of numbers for multiselect.
|
* it will be an array of numbers and optionally an action string for multichoice
|
||||||
|
* dialogs
|
||||||
*/
|
*/
|
||||||
export type ChoiceAnswer = undefined | number | number[];
|
export type ChoiceAnswer = undefined | { action?: string; items: number | number[]};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dialog with choice fields.
|
* A dialog with choice fields.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'os-choice-dialog',
|
selector: 'os-choice-dialog',
|
||||||
@ -81,9 +95,15 @@ export class ChoiceDialogComponent {
|
|||||||
/**
|
/**
|
||||||
* Closes the dialog with the selected choices
|
* Closes the dialog with the selected choices
|
||||||
*/
|
*/
|
||||||
public closeDialog(ok: boolean): void {
|
public closeDialog(ok: boolean, action?: string): void {
|
||||||
|
if (!this.data.multiSelect && this.selectedChoice === null) {
|
||||||
|
action = this.data.clearChoice;
|
||||||
|
}
|
||||||
if (ok) {
|
if (ok) {
|
||||||
this.dialogRef.close(this.data.multiSelect ? this.selectedMultiChoices : this.selectedChoice);
|
this.dialogRef.close({
|
||||||
|
action: action ? action : null,
|
||||||
|
items: this.data.multiSelect ? this.selectedMultiChoices : this.selectedChoice
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,6 @@
|
|||||||
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="extra-controls-slot on-transition-fade" *ngIf="isMultiSelect">
|
|
||||||
<button mat-icon-button (click)="deleteSelected()"><mat-icon>delete</mat-icon></button>
|
|
||||||
</div>
|
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
|
||||||
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
||||||
@ -110,12 +107,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="isMultiSelect">
|
<div *ngIf="isMultiSelect">
|
||||||
<!-- Exit multi select -->
|
|
||||||
<button mat-menu-item (click)="toggleMultiSelect()">
|
|
||||||
<mat-icon>library_add</mat-icon>
|
|
||||||
<span translate>Exit multiselect</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Select all -->
|
<!-- Select all -->
|
||||||
<button mat-menu-item (click)="selectAll()">
|
<button mat-menu-item (click)="selectAll()">
|
||||||
<mat-icon>done_all</mat-icon>
|
<mat-icon>done_all</mat-icon>
|
||||||
|
@ -13,10 +13,6 @@
|
|||||||
<button mat-icon-button (click)="toggleMultiSelect()"><mat-icon>arrow_back</mat-icon></button>
|
<button mat-icon-button (click)="toggleMultiSelect()"><mat-icon>arrow_back</mat-icon></button>
|
||||||
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="extra-controls-slot on-transition-fade" *ngIf="isMultiSelect">
|
|
||||||
<button mat-icon-button (click)="deleteSelected()"><mat-icon>delete</mat-icon></button>
|
|
||||||
</div>
|
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
|
||||||
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
||||||
@ -40,10 +36,6 @@
|
|||||||
<mat-chip color="primary" selected>{{ assignment.phase }}</mat-chip>
|
<mat-chip color="primary" selected>{{ assignment.phase }}</mat-chip>
|
||||||
</mat-chip-list>
|
</mat-chip-list>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
<button mat-menu-item (click)="toggleMultiSelect()">
|
|
||||||
<mat-icon>library_add</mat-icon>
|
|
||||||
<span translate>Exit multiselect</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="selectAll()">
|
<button mat-menu-item (click)="selectAll()">
|
||||||
<mat-icon>done_all</mat-icon>
|
<mat-icon>done_all</mat-icon>
|
||||||
<span translate>Select all</span>
|
<span translate>Select all</span>
|
||||||
@ -87,10 +79,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="isMultiSelect">
|
<div *ngIf="isMultiSelect">
|
||||||
<button mat-menu-item (click)="toggleMultiSelect()">
|
|
||||||
<mat-icon>library_add</mat-icon>
|
|
||||||
<span translate>Exit multiselect</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="selectAll()">
|
<button mat-menu-item (click)="selectAll()">
|
||||||
<mat-icon>done_all</mat-icon>
|
<mat-icon>done_all</mat-icon>
|
||||||
<span translate>Select all</span>
|
<span translate>Select all</span>
|
||||||
|
@ -46,9 +46,6 @@
|
|||||||
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="extra-controls-slot on-transition-fade" *ngIf="isMultiSelect">
|
|
||||||
<button mat-icon-button (click)="deleteSelected()"><mat-icon>delete</mat-icon></button>
|
|
||||||
</div>
|
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
|
||||||
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
||||||
@ -160,10 +157,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div *ngIf="isMultiSelect">
|
<div *ngIf="isMultiSelect">
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<button mat-menu-item (click)="toggleMultiSelect()">
|
|
||||||
<mat-icon>library_add</mat-icon>
|
|
||||||
<span translate>Exit multiselect</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="selectAll()">
|
<button mat-menu-item (click)="selectAll()">
|
||||||
<mat-icon>done_all</mat-icon>
|
<mat-icon>done_all</mat-icon>
|
||||||
<span translate>Select all</span>
|
<span translate>Select all</span>
|
||||||
|
@ -152,12 +152,12 @@
|
|||||||
<mat-icon>speaker_notes</mat-icon>
|
<mat-icon>speaker_notes</mat-icon>
|
||||||
<span translate>Comment fields</span>
|
<span translate>Comment fields</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button mat-menu-item (click)="csvExportMotionList()">
|
||||||
|
<mat-icon>archive</mat-icon>
|
||||||
|
<span translate>Export as CSV</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="isMultiSelect">
|
<div *ngIf="isMultiSelect">
|
||||||
<button mat-menu-item (click)="toggleMultiSelect()">
|
|
||||||
<mat-icon>library_add</mat-icon>
|
|
||||||
<span translate>Exit multiselect</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="selectAll()">
|
<button mat-menu-item (click)="selectAll()">
|
||||||
<mat-icon>done_all</mat-icon>
|
<mat-icon>done_all</mat-icon>
|
||||||
<span translate>Select all</span>
|
<span translate>Select all</span>
|
||||||
@ -168,53 +168,47 @@
|
|||||||
</button>
|
</button>
|
||||||
<div *osPerms="'motions.can_manage'">
|
<div *osPerms="'motions.can_manage'">
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
<button mat-menu-item (click)="multiselectWrapper(multiselectService.setStateOfMultiple(selectedRows))">
|
||||||
|
<mat-icon>label</mat-icon>
|
||||||
|
<span translate>Set status</span>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="recomendationEnabled" 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 *ngIf="categories.length"
|
||||||
|
(click)="multiselectWrapper(multiselectService.setCategory(selectedRows))">
|
||||||
|
<mat-icon>device_hub</mat-icon>
|
||||||
|
<!-- TODO: icon -->
|
||||||
|
<span translate>Set category</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item *ngIf="motionBlocks.length"
|
||||||
|
(click)="multiselectWrapper(multiselectService.setMotionBlock(selectedRows))">
|
||||||
|
<mat-icon>widgets</mat-icon>
|
||||||
|
<!-- TODO: icon -->
|
||||||
|
<span translate>Set motion block</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button mat-menu-item (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"
|
||||||
|
(click)="multiselectWrapper(multiselectService.changeTags(selectedRows))">
|
||||||
|
<mat-icon>bookmarks</mat-icon>
|
||||||
|
<!-- TODO: icon -->
|
||||||
|
<span translate>Add/remove tags</span>
|
||||||
|
</button>
|
||||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.moveToItem(selectedRows))">
|
<button mat-menu-item (click)="multiselectWrapper(multiselectService.moveToItem(selectedRows))">
|
||||||
<!-- TODO: Not implemented yet -->
|
<!-- TODO: Not implemented yet -->
|
||||||
<mat-icon>sort</mat-icon>
|
<mat-icon>sort</mat-icon>
|
||||||
<span translate>Move to agenda item</span>
|
<span translate>Move to agenda item</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.setStateOfMultiple(selectedRows))">
|
|
||||||
<mat-icon>label</mat-icon>
|
|
||||||
<span translate>Set status</span>
|
|
||||||
</button>
|
|
||||||
<button 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 (click)="multiselectWrapper(multiselectService.setCategory(selectedRows))">
|
|
||||||
<mat-icon>device_hub</mat-icon>
|
|
||||||
<!-- TODO: icon -->
|
|
||||||
<span translate>Set category</span>
|
|
||||||
</button>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.addSubmitters(selectedRows))">
|
|
||||||
<mat-icon>person_add</mat-icon>
|
|
||||||
<!-- TODO: icon -->
|
|
||||||
<span translate>Add submitters</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.removeSubmitters(selectedRows))">
|
|
||||||
<mat-icon>person_outline</mat-icon>
|
|
||||||
<!-- TODO: icon -->
|
|
||||||
<span translate>remove submitters</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.addTags(selectedRows))">
|
|
||||||
<mat-icon>bookmarks</mat-icon>
|
|
||||||
<!-- TODO: icon -->
|
|
||||||
<span translate>Add tags</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.removeTags(selectedRows))">
|
|
||||||
<mat-icon>bookmark_border</mat-icon>
|
|
||||||
<!-- TODO: icon -->
|
|
||||||
<span translate>Remove tags</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<button mat-menu-item (click)="csvExportMotionList(); toggleMultiSelect()">
|
|
||||||
<mat-icon>archive</mat-icon>
|
|
||||||
<span translate>Export as CSV</span>
|
|
||||||
</button>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<button
|
<button
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
class="red-warning-text"
|
class="red-warning-text"
|
||||||
|
@ -11,6 +11,14 @@ import { MotionRepositoryService } from '../../services/motion-repository.servic
|
|||||||
import { ViewMotion } from '../../models/view-motion';
|
import { ViewMotion } from '../../models/view-motion';
|
||||||
import { WorkflowState } from '../../../../shared/models/motions/workflow-state';
|
import { WorkflowState } from '../../../../shared/models/motions/workflow-state';
|
||||||
import { MotionMultiselectService } from '../../services/motion-multiselect.service';
|
import { MotionMultiselectService } from '../../services/motion-multiselect.service';
|
||||||
|
import { TagRepositoryService } from 'app/site/tags/services/tag-repository.service';
|
||||||
|
import { MotionBlockRepositoryService } from '../../services/motion-block-repository.service';
|
||||||
|
import { CategoryRepositoryService } from '../../services/category-repository.service';
|
||||||
|
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||||
|
import { ViewWorkflow } from '../../models/view-workflow';
|
||||||
|
import { ViewCategory } from '../../models/view-category';
|
||||||
|
import { ViewMotionBlock } from '../../models/view-motion-block';
|
||||||
|
import { WorkflowRepositoryService } from '../../services/workflow-repository.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays all the motions in a Table using DataSource.
|
* Component that displays all the motions in a Table using DataSource.
|
||||||
@ -39,6 +47,13 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
* @TODO replace by direct access to config variable, once it's available from the templates
|
* @TODO replace by direct access to config variable, once it's available from the templates
|
||||||
*/
|
*/
|
||||||
public statutesEnabled: boolean;
|
public statutesEnabled: boolean;
|
||||||
|
public recomendationEnabled: boolean;
|
||||||
|
|
||||||
|
|
||||||
|
public tags: ViewTag[] = [];
|
||||||
|
public workflows: ViewWorkflow[] = [];
|
||||||
|
public categories: ViewCategory[] = [];
|
||||||
|
public motionBlocks: ViewMotionBlock[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor implements title and translation Module.
|
* Constructor implements title and translation Module.
|
||||||
@ -50,13 +65,11 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
* @param route Current route
|
* @param route Current route
|
||||||
* @param configService The configuration provider
|
* @param configService The configuration provider
|
||||||
* @param repo Motion Repository
|
* @param repo Motion Repository
|
||||||
* @param promptService
|
* @param tagRepo Tag Repository
|
||||||
* @param motionCsvExport
|
* @param motionBlockRepo
|
||||||
* @param workflowRepo Workflow Repository
|
|
||||||
* @param categoryRepo
|
* @param categoryRepo
|
||||||
* @param userRepo
|
* @param motionCsvExport
|
||||||
* @param tagRepo
|
* @param multiselectService Service for the multiSelect actions
|
||||||
* @param choiceService
|
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
titleService: Title,
|
titleService: Title,
|
||||||
@ -66,6 +79,10 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private repo: MotionRepositoryService,
|
private repo: MotionRepositoryService,
|
||||||
|
private tagRepo: TagRepositoryService,
|
||||||
|
private motionBlockRepo: MotionBlockRepositoryService,
|
||||||
|
private categoryRepo: CategoryRepositoryService,
|
||||||
|
private workflowRepo: WorkflowRepositoryService,
|
||||||
private motionCsvExport: MotionCsvExportService,
|
private motionCsvExport: MotionCsvExportService,
|
||||||
public multiselectService: MotionMultiselectService
|
public multiselectService: MotionMultiselectService
|
||||||
) {
|
) {
|
||||||
@ -84,7 +101,6 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
super.setTitle('Motions');
|
super.setTitle('Motions');
|
||||||
this.initTable();
|
this.initTable();
|
||||||
this.repo.getViewModelListObservable().subscribe(newMotions => {
|
this.repo.getViewModelListObservable().subscribe(newMotions => {
|
||||||
this.checkSelection();
|
|
||||||
// TODO: This is for testing purposes. Can be removed with #3963
|
// TODO: This is for testing purposes. Can be removed with #3963
|
||||||
this.dataSource.data = newMotions.sort((a, b) => {
|
this.dataSource.data = newMotions.sort((a, b) => {
|
||||||
if (a.callListWeight !== b.callListWeight) {
|
if (a.callListWeight !== b.callListWeight) {
|
||||||
@ -93,8 +109,14 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
return a.id - b.id;
|
return a.id - b.id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.checkSelection();
|
||||||
});
|
});
|
||||||
this.configService.get('motions_statutes_enabled').subscribe(enabled => (this.statutesEnabled = enabled));
|
this.configService.get('motions_statutes_enabled').subscribe(enabled => (this.statutesEnabled = enabled));
|
||||||
|
this.configService.get('motions_recommendations_by').subscribe(id => (this.recomendationEnabled = !!id));
|
||||||
|
this.motionBlockRepo.getViewModelListObservable().subscribe(mBs => this.motionBlocks = mBs);
|
||||||
|
this.categoryRepo.getViewModelListObservable().subscribe(cats => this.categories = cats);
|
||||||
|
this.tagRepo.getViewModelListObservable().subscribe(tags => this.tags = tags);
|
||||||
|
this.workflowRepo.getViewModelListObservable().subscribe(wfs => this.workflows = wfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,7 +200,11 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
*
|
*
|
||||||
* @param multiselectPromise The promise returned by multiselect actions.
|
* @param multiselectPromise The promise returned by multiselect actions.
|
||||||
*/
|
*/
|
||||||
public multiselectWrapper(multiselectPromise: Promise<void>): void {
|
public async multiselectWrapper(multiselectPromise: Promise<void>): Promise<void> {
|
||||||
multiselectPromise.then(() => this.toggleMultiSelect(), this.raiseError);
|
try {
|
||||||
|
await multiselectPromise;
|
||||||
|
} catch (e) {
|
||||||
|
this.raiseError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import { HttpService } from 'app/core/services/http.service';
|
|||||||
import { AgendaRepositoryService } from 'app/site/agenda/services/agenda-repository.service';
|
import { AgendaRepositoryService } from 'app/site/agenda/services/agenda-repository.service';
|
||||||
import { Displayable } from 'app/shared/models/base/displayable';
|
import { Displayable } from 'app/shared/models/base/displayable';
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
|
import { MotionBlockRepositoryService } from './motion-block-repository.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all multiselect actions for the motion list view.
|
* Contains all multiselect actions for the motion list view.
|
||||||
@ -33,6 +34,9 @@ export class MotionMultiselectService {
|
|||||||
* @param workflowRepo
|
* @param workflowRepo
|
||||||
* @param categoryRepo
|
* @param categoryRepo
|
||||||
* @param tagRepo
|
* @param tagRepo
|
||||||
|
* @param agendaRepo
|
||||||
|
* @param motionBlockRepo
|
||||||
|
* @param httpService
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
private repo: MotionRepositoryService,
|
private repo: MotionRepositoryService,
|
||||||
@ -44,6 +48,7 @@ export class MotionMultiselectService {
|
|||||||
private categoryRepo: CategoryRepositoryService,
|
private categoryRepo: CategoryRepositoryService,
|
||||||
private tagRepo: TagRepositoryService,
|
private tagRepo: TagRepositoryService,
|
||||||
private agendaRepo: AgendaRepositoryService,
|
private agendaRepo: AgendaRepositoryService,
|
||||||
|
private motionBlockRepo: MotionBlockRepositoryService,
|
||||||
private httpService: HttpService
|
private httpService: HttpService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -71,7 +76,7 @@ export class MotionMultiselectService {
|
|||||||
if (selectedChoice) {
|
if (selectedChoice) {
|
||||||
const requestData = {
|
const requestData = {
|
||||||
items: motions.map(motion => motion.agenda_item_id),
|
items: motions.map(motion => motion.agenda_item_id),
|
||||||
parent_id: selectedChoice as number
|
parent_id: selectedChoice.items as number
|
||||||
};
|
};
|
||||||
await this.httpService.post('/rest/agenda/item/assign', requestData);
|
await this.httpService.post('/rest/agenda/item/assign', requestData);
|
||||||
}
|
}
|
||||||
@ -84,14 +89,15 @@ export class MotionMultiselectService {
|
|||||||
*/
|
*/
|
||||||
public async setStateOfMultiple(motions: ViewMotion[]): Promise<void> {
|
public async setStateOfMultiple(motions: ViewMotion[]): Promise<void> {
|
||||||
const title = this.translate.instant('This will set the state of all selected motions to:');
|
const title = this.translate.instant('This will set the state of all selected motions to:');
|
||||||
const choices = this.workflowRepo.getAllWorkflowStates().map(workflowState => ({
|
const choices = this.workflowRepo.getWorkflowStatesForMotions(motions)
|
||||||
id: workflowState.id,
|
.map(workflowState => ({
|
||||||
label: workflowState.name
|
id: workflowState.id,
|
||||||
}));
|
label: workflowState.name
|
||||||
|
}));
|
||||||
const selectedChoice = await this.choiceService.open(title, choices);
|
const selectedChoice = await this.choiceService.open(title, choices);
|
||||||
if (selectedChoice) {
|
if (selectedChoice) {
|
||||||
for (const motion of motions) {
|
for (const motion of motions) {
|
||||||
await this.repo.setState(motion, selectedChoice as number);
|
await this.repo.setState(motion, selectedChoice.items as number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,18 +110,19 @@ export class MotionMultiselectService {
|
|||||||
public async setRecommendation(motions: ViewMotion[]): Promise<void> {
|
public async setRecommendation(motions: ViewMotion[]): Promise<void> {
|
||||||
const title = this.translate.instant('This will set the recommendation for all selected motions to:');
|
const title = this.translate.instant('This will set the recommendation for all selected motions to:');
|
||||||
const choices = this.workflowRepo
|
const choices = this.workflowRepo
|
||||||
.getAllWorkflowStates()
|
.getWorkflowStatesForMotions(motions)
|
||||||
.filter(workflowState => !!workflowState.recommendation_label)
|
.filter(workflowState => !!workflowState.recommendation_label)
|
||||||
.map(workflowState => ({
|
.map(workflowState => ({
|
||||||
id: workflowState.id,
|
id: workflowState.id,
|
||||||
label: workflowState.recommendation_label
|
label: workflowState.recommendation_label
|
||||||
}));
|
}));
|
||||||
choices.push({ id: 0, label: 'Delete recommendation' });
|
const clearChoice = 'Delete recommendation';
|
||||||
const selectedChoice = await this.choiceService.open(title, choices);
|
const selectedChoice = await this.choiceService.open(title, choices, false,
|
||||||
if (typeof selectedChoice === 'number') {
|
null, clearChoice);
|
||||||
|
if (selectedChoice) {
|
||||||
const requestData = motions.map(motion => ({
|
const requestData = motions.map(motion => ({
|
||||||
id: motion.id,
|
id: motion.id,
|
||||||
recommendation: selectedChoice
|
recommendation: selectedChoice.action ? 0 : selectedChoice.items as number
|
||||||
}));
|
}));
|
||||||
await this.httpService.post('/rest/motions/motion/manage_multiple_recommendation', {
|
await this.httpService.post('/rest/motions/motion/manage_multiple_recommendation', {
|
||||||
motions: requestData
|
motions: requestData
|
||||||
@ -130,25 +137,31 @@ export class MotionMultiselectService {
|
|||||||
*/
|
*/
|
||||||
public async setCategory(motions: ViewMotion[]): Promise<void> {
|
public async setCategory(motions: ViewMotion[]): Promise<void> {
|
||||||
const title = this.translate.instant('This will set the category of all selected motions to:');
|
const title = this.translate.instant('This will set the category of all selected motions to:');
|
||||||
const selectedChoice = await this.choiceService.open(title, this.categoryRepo.getViewModelList());
|
const clearChoice = 'No category';
|
||||||
|
const selectedChoice = await this.choiceService.open(title, this.categoryRepo.getViewModelList(),
|
||||||
|
false, null, clearChoice);
|
||||||
if (selectedChoice) {
|
if (selectedChoice) {
|
||||||
for (const motion of motions) {
|
for (const motion of motions) {
|
||||||
await this.repo.update({ category_id: selectedChoice as number }, motion);
|
await this.repo.update(
|
||||||
|
{category_id: selectedChoice.action ? 0 : selectedChoice.items as number },
|
||||||
|
motion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a dialog and adds the selected submitters for all given motions.
|
* Opens a dialog and adds or removes the selected submitters for all given motions.
|
||||||
*
|
*
|
||||||
* @param motions The motions to add the sumbitters to
|
* @param motions The motions to add/remove the sumbitters to
|
||||||
*/
|
*/
|
||||||
public async addSubmitters(motions: ViewMotion[]): Promise<void> {
|
public async changeSubmitters(motions: ViewMotion[]): Promise<void> {
|
||||||
const title = this.translate.instant('This will add the following submitters of all selected motions:');
|
const title = this.translate.instant('This will add or remove the following submitters for all selected motions:');
|
||||||
const selectedChoice = await this.choiceService.open(title, this.userRepo.getViewModelList(), true);
|
const choices = ['Add', 'Remove'];
|
||||||
if (selectedChoice) {
|
const selectedChoice = await this.choiceService.open(title,
|
||||||
|
this.userRepo.getViewModelList(), true, choices);
|
||||||
|
if (selectedChoice && selectedChoice.action === choices[0]) {
|
||||||
const requestData = motions.map(motion => {
|
const requestData = motions.map(motion => {
|
||||||
let submitterIds = [...motion.submitters_id, ...(selectedChoice as number[])];
|
let submitterIds = [...motion.submitters_id, ...(selectedChoice.items as number[])];
|
||||||
submitterIds = submitterIds.filter((id, index, self) => self.indexOf(id) === index); // remove duplicates
|
submitterIds = submitterIds.filter((id, index, self) => self.indexOf(id) === index); // remove duplicates
|
||||||
return {
|
return {
|
||||||
id: motion.id,
|
id: motion.id,
|
||||||
@ -156,20 +169,9 @@ export class MotionMultiselectService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
await this.httpService.post('/rest/motions/motion/manage_multiple_submitters', { motions: requestData });
|
await this.httpService.post('/rest/motions/motion/manage_multiple_submitters', { motions: requestData });
|
||||||
}
|
} else if (selectedChoice && selectedChoice.action === choices[1]) {
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens a dialog and removes the selected submitters for all given motions.
|
|
||||||
*
|
|
||||||
* @param motions The motions to remove the submitters from
|
|
||||||
*/
|
|
||||||
public async removeSubmitters(motions: ViewMotion[]): Promise<void> {
|
|
||||||
const title = this.translate.instant('This will remove the following submitters from all selected motions:');
|
|
||||||
const selectedChoice = await this.choiceService.open(title, this.userRepo.getViewModelList(), true);
|
|
||||||
if (selectedChoice) {
|
|
||||||
const requestData = motions.map(motion => {
|
const requestData = motions.map(motion => {
|
||||||
const submitterIdsToRemove = selectedChoice as number[];
|
const submitterIdsToRemove = selectedChoice.items as number[];
|
||||||
const submitterIds = motion.submitters_id.filter(id => !submitterIdsToRemove.includes(id));
|
const submitterIds = motion.submitters_id.filter(id => !submitterIdsToRemove.includes(id));
|
||||||
return {
|
return {
|
||||||
id: motion.id,
|
id: motion.id,
|
||||||
@ -181,16 +183,18 @@ export class MotionMultiselectService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a dialog and adds the selected tags for all given motions.
|
* Opens a dialog and adds/removes the selected tags for all given motions.
|
||||||
*
|
*
|
||||||
* @param motions The motions to add the tags to
|
* @param motions The motions to add the tags to
|
||||||
*/
|
*/
|
||||||
public async addTags(motions: ViewMotion[]): Promise<void> {
|
public async changeTags(motions: ViewMotion[]): Promise<void> {
|
||||||
const title = this.translate.instant('This will add the following tags to all selected motions:');
|
const title = this.translate.instant('This will add or remove the following tags for all selected motions:');
|
||||||
const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true);
|
const choices = ['Add', 'Remove', 'Clear tags'];
|
||||||
if (selectedChoice) {
|
const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true,
|
||||||
|
choices);
|
||||||
|
if (selectedChoice && selectedChoice.action === choices[0]) {
|
||||||
const requestData = motions.map(motion => {
|
const requestData = motions.map(motion => {
|
||||||
let tagIds = [...motion.tags_id, ...(selectedChoice as number[])];
|
let tagIds = [...motion.tags_id, ...(selectedChoice.items as number[])];
|
||||||
tagIds = tagIds.filter((id, index, self) => self.indexOf(id) === index); // remove duplicates
|
tagIds = tagIds.filter((id, index, self) => self.indexOf(id) === index); // remove duplicates
|
||||||
return {
|
return {
|
||||||
id: motion.id,
|
id: motion.id,
|
||||||
@ -198,20 +202,9 @@ export class MotionMultiselectService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
await this.httpService.post('/rest/motions/motion/manage_multiple_tags', { motions: requestData });
|
await this.httpService.post('/rest/motions/motion/manage_multiple_tags', { motions: requestData });
|
||||||
}
|
} else if (selectedChoice && selectedChoice.action === choices[1]) {
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens a dialog and removes the selected tags for all given motions.
|
|
||||||
*
|
|
||||||
* @param motions The motions to remove the tags from
|
|
||||||
*/
|
|
||||||
public async removeTags(motions: ViewMotion[]): Promise<void> {
|
|
||||||
const title = this.translate.instant('This will remove the following tags from all selected motions:');
|
|
||||||
const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true);
|
|
||||||
if (selectedChoice) {
|
|
||||||
const requestData = motions.map(motion => {
|
const requestData = motions.map(motion => {
|
||||||
const tagIdsToRemove = selectedChoice as number[];
|
const tagIdsToRemove = selectedChoice.items as number[];
|
||||||
const tagIds = motion.tags_id.filter(id => !tagIdsToRemove.includes(id));
|
const tagIds = motion.tags_id.filter(id => !tagIdsToRemove.includes(id));
|
||||||
return {
|
return {
|
||||||
id: motion.id,
|
id: motion.id,
|
||||||
@ -219,6 +212,33 @@ export class MotionMultiselectService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
await this.httpService.post('/rest/motions/motion/manage_multiple_tags', { motions: requestData });
|
await this.httpService.post('/rest/motions/motion/manage_multiple_tags', { motions: requestData });
|
||||||
|
} else if (selectedChoice && selectedChoice.action === choices[2]) {
|
||||||
|
const requestData = motions.map(motion => {
|
||||||
|
return {
|
||||||
|
id: motion.id,
|
||||||
|
tags: []
|
||||||
|
};
|
||||||
|
});
|
||||||
|
await this.httpService.post('/rest/motions/motion/manage_multiple_tags', { motions: requestData });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a dialog and changes the motionBlock for all given motions.
|
||||||
|
*
|
||||||
|
* @param motions The motions for which to change the motionBlock
|
||||||
|
*/
|
||||||
|
public async setMotionBlock(motions: ViewMotion[]): Promise<void> {
|
||||||
|
const title = this.translate.instant('This will change the motion Block for all selected motions:');
|
||||||
|
const clearChoice = 'Clear motion block';
|
||||||
|
const selectedChoice = await this.choiceService.open(title, this.motionBlockRepo.getViewModelList(),
|
||||||
|
false, null, clearChoice);
|
||||||
|
if (selectedChoice) {
|
||||||
|
for (const motion of motions) {
|
||||||
|
const blockId = selectedChoice.action ? null : selectedChoice.items as number;
|
||||||
|
await this.repo.update({motion_block_id: blockId}, motion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import { BaseRepository } from '../../base/base-repository';
|
|||||||
import { Identifiable } from '../../../shared/models/base/identifiable';
|
import { Identifiable } from '../../../shared/models/base/identifiable';
|
||||||
import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service';
|
import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service';
|
||||||
import { WorkflowState } from 'app/shared/models/motions/workflow-state';
|
import { WorkflowState } from 'app/shared/models/motions/workflow-state';
|
||||||
|
import { ViewMotion } from '../models/view-motion';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for Categories
|
* Repository Services for Categories
|
||||||
@ -70,4 +71,18 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
|
|||||||
});
|
});
|
||||||
return states;
|
return states;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all workflowStates that cover the list of viewMotions given
|
||||||
|
* @param motions
|
||||||
|
*/
|
||||||
|
public getWorkflowStatesForMotions(motions: ViewMotion[]): WorkflowState[] {
|
||||||
|
let states: WorkflowState[] = [];
|
||||||
|
const workflowIds = motions.map(motion => motion.workflow_id).filter((value, index, self) => self.indexOf(value) === index);
|
||||||
|
workflowIds.forEach(id => {
|
||||||
|
const workflow = this.getViewModel(id);
|
||||||
|
states = states.concat(workflow.states);
|
||||||
|
});
|
||||||
|
return states;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,6 @@
|
|||||||
<button mat-icon-button (click)="toggleMultiSelect()"><mat-icon>arrow_back</mat-icon></button>
|
<button mat-icon-button (click)="toggleMultiSelect()"><mat-icon>arrow_back</mat-icon></button>
|
||||||
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
<span>{{ selectedRows.length }} </span><span translate>selected</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="extra-controls-slot on-transition-fade" *ngIf="isMultiSelect">
|
|
||||||
<button mat-icon-button (click)="deleteSelected()"><mat-icon>delete</mat-icon></button>
|
|
||||||
</div>
|
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
|
||||||
<div class="custom-table-header on-transition-fade">
|
<div class="custom-table-header on-transition-fade">
|
||||||
@ -100,10 +96,6 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="isMultiSelect">
|
<div *ngIf="isMultiSelect">
|
||||||
<button mat-menu-item (click)="toggleMultiSelect()">
|
|
||||||
<mat-icon>library_add</mat-icon>
|
|
||||||
<span translate>Exit multiselect</span>
|
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="selectAll()">
|
<button mat-menu-item (click)="selectAll()">
|
||||||
<mat-icon>done_all</mat-icon>
|
<mat-icon>done_all</mat-icon>
|
||||||
<span translate>Select all</span>
|
<span translate>Select all</span>
|
||||||
@ -114,42 +106,23 @@
|
|||||||
</button>
|
</button>
|
||||||
<div *osPerms="'users.can_manage'">
|
<div *osPerms="'users.can_manage'">
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<button mat-menu-item (click)="setGroupSelected(true)">
|
<button mat-menu-item (click)="setGroupSelected()">
|
||||||
<mat-icon>people</mat-icon>
|
<mat-icon>people</mat-icon>
|
||||||
<span translate>Add groups</span>
|
<span translate>Add/remove groups</span>
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="setGroupSelected(false)">
|
|
||||||
<mat-icon>people_outline</mat-icon>
|
|
||||||
<span translate>Remove groups</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<mat-divider></mat-divider>
|
<button mat-menu-item (click)="setActiveSelected()">
|
||||||
|
|
||||||
<button mat-menu-item (click)="setActiveSelected(true)">
|
|
||||||
<mat-icon>add_circle</mat-icon>
|
<mat-icon>add_circle</mat-icon>
|
||||||
<span translate>Is active</span>
|
<span translate>Set/unset active</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item (click)="setActiveSelected(false)">
|
|
||||||
<mat-icon>remove_circle</mat-icon>
|
<button mat-menu-item (click)="setPresentSelected()">
|
||||||
<span translate>Is inactive</span>
|
|
||||||
</button>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<button mat-menu-item (click)="setPresentSelected(true)">
|
|
||||||
<mat-icon>check_box</mat-icon>
|
<mat-icon>check_box</mat-icon>
|
||||||
<span translate>Is present</span>
|
<span translate>Set/unset presence</span>
|
||||||
</button>
|
</button>
|
||||||
<button mat-menu-item (click)="setPresentSelected(false)">
|
<button mat-menu-item (click)="setCommitteeSelected()">
|
||||||
<mat-icon>check_box_outline_blank</mat-icon>
|
|
||||||
<span translate>Is not present</span>
|
|
||||||
</button>
|
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<button mat-menu-item (click)="setCommitteeSelected(true)">
|
|
||||||
<mat-icon>account_balance</mat-icon>
|
<mat-icon>account_balance</mat-icon>
|
||||||
<span translate>Is committee</span>
|
<span translate>Set/unset committee</span>
|
||||||
</button>
|
|
||||||
<button mat-menu-item (click)="setCommitteeSelected(false)">
|
|
||||||
<mat-icon>account_balance</mat-icon>
|
|
||||||
<span translate>Is no committee</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
|
@ -131,23 +131,20 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
|||||||
* Opens a dialog and sets the group(s) for all selected users.
|
* Opens a dialog and sets the group(s) for all selected users.
|
||||||
* SelectedRows is only filled with data in multiSelect mode
|
* SelectedRows is only filled with data in multiSelect mode
|
||||||
*/
|
*/
|
||||||
public async setGroupSelected(add: boolean): Promise<void> {
|
public async setGroupSelected(): Promise<void> {
|
||||||
let content: string;
|
const content = this.translate.instant('This will add or remove the following groups for all selected users:');
|
||||||
if (add) {
|
const choices = ['Add group(s)', 'Remove group(s)'];
|
||||||
content = this.translate.instant('This will add the following groups to all selected users:');
|
const selectedChoice = await this.choiceService.open(content,
|
||||||
} else {
|
this.groupRepo.getViewModelList(), true, choices);
|
||||||
content = this.translate.instant('This will remove the following groups from all selected users:');
|
|
||||||
}
|
|
||||||
const selectedChoice = await this.choiceService.open(content, this.groupRepo.getViewModelList(), true);
|
|
||||||
if (selectedChoice) {
|
if (selectedChoice) {
|
||||||
for (const user of this.selectedRows) {
|
for (const user of this.selectedRows) {
|
||||||
const newGroups = [...user.groups_id];
|
const newGroups = [...user.groups_id];
|
||||||
(selectedChoice as number[]).forEach(newChoice => {
|
(selectedChoice.items as number[]).forEach(newChoice => {
|
||||||
const idx = newGroups.indexOf(newChoice);
|
const idx = newGroups.indexOf(newChoice);
|
||||||
if (idx < 0 && add) {
|
if (idx < 0 && selectedChoice.action === choices[0]) {
|
||||||
newGroups.push(newChoice);
|
newGroups.push(newChoice);
|
||||||
} else if (idx >= 0 && !add) {
|
} else if (idx >= 0 && selectedChoice.action === choices[1]) {
|
||||||
newGroups.slice(idx, 1);
|
newGroups.splice(idx, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await this.repo.update({ groups_id: newGroups }, user);
|
await this.repo.update({ groups_id: newGroups }, user);
|
||||||
@ -159,9 +156,15 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
|||||||
* Handler for bulk setting/unsetting the 'active' attribute.
|
* Handler for bulk setting/unsetting the 'active' attribute.
|
||||||
* Uses selectedRows defined via multiSelect mode.
|
* Uses selectedRows defined via multiSelect mode.
|
||||||
*/
|
*/
|
||||||
public async setActiveSelected(active: boolean): Promise<void> {
|
public async setActiveSelected(): Promise<void> {
|
||||||
for (const user of this.selectedRows) {
|
const content = this.translate.instant('Set the active status for the selected users');
|
||||||
await this.repo.update({ is_active: active }, user);
|
const options = ['Active', 'Not active'];
|
||||||
|
const selectedChoice = await this.choiceService.open(content, null, false, options);
|
||||||
|
if (selectedChoice) {
|
||||||
|
const active = selectedChoice.action === options[0];
|
||||||
|
for (const user of this.selectedRows) {
|
||||||
|
await this.repo.update({ is_active: active }, user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,9 +172,15 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
|||||||
* Handler for bulk setting/unsetting the 'is present' attribute.
|
* Handler for bulk setting/unsetting the 'is present' attribute.
|
||||||
* Uses selectedRows defined via multiSelect mode.
|
* Uses selectedRows defined via multiSelect mode.
|
||||||
*/
|
*/
|
||||||
public async setPresentSelected(present: boolean): Promise<void> {
|
public async setPresentSelected(): Promise<void> {
|
||||||
for (const user of this.selectedRows) {
|
const content = this.translate.instant('Set the presence status for the selected users');
|
||||||
await this.repo.update({ is_present: present }, user);
|
const options = ['Present', 'Not present'];
|
||||||
|
const selectedChoice = await this.choiceService.open(content, null, false, options);
|
||||||
|
if (selectedChoice) {
|
||||||
|
const present = selectedChoice.action === options[0];
|
||||||
|
for (const user of this.selectedRows) {
|
||||||
|
await this.repo.update({ is_present: present }, user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,9 +188,16 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
|||||||
* Handler for bulk setting/unsetting the 'is committee' attribute.
|
* Handler for bulk setting/unsetting the 'is committee' attribute.
|
||||||
* Uses selectedRows defined via multiSelect mode.
|
* Uses selectedRows defined via multiSelect mode.
|
||||||
*/
|
*/
|
||||||
public async setCommitteeSelected(is_committee: boolean): Promise<void> {
|
public async setCommitteeSelected(): Promise<void> {
|
||||||
for (const user of this.selectedRows) {
|
const content = this.translate.instant(
|
||||||
await this.repo.update({ is_committee: is_committee }, user);
|
'Sets/unsets the committee status for the selected users');
|
||||||
|
const options = ['Is committee', 'Is not committee'];
|
||||||
|
const selectedChoice = await this.choiceService.open(content, null, false, options);
|
||||||
|
if (selectedChoice) {
|
||||||
|
const committee = selectedChoice.action === options[0];
|
||||||
|
for (const user of this.selectedRows) {
|
||||||
|
await this.repo.update({ is_committee: committee }, user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +210,7 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for bulk resetting passwords. Needs multiSelect mode.
|
* Handler for bulk setting new passwords. Needs multiSelect mode.
|
||||||
*/
|
*/
|
||||||
public async resetPasswordsSelected(): Promise<void> {
|
public async resetPasswordsSelected(): Promise<void> {
|
||||||
for (const user of this.selectedRows) {
|
for (const user of this.selectedRows) {
|
||||||
|
@ -111,7 +111,8 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the password and sets the password without checking for the old one
|
* Updates the password and sets the password without checking for the old one.
|
||||||
|
* Also resets the 'default password' to the newly created one.
|
||||||
*
|
*
|
||||||
* @param user The user to update
|
* @param user The user to update
|
||||||
* @param password The password to set
|
* @param password The password to set
|
||||||
@ -119,6 +120,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
|
|||||||
public async resetPassword(user: ViewUser, password: string): Promise<void> {
|
public async resetPassword(user: ViewUser, password: string): Promise<void> {
|
||||||
const path = `/rest/users/user/${user.id}/reset_password/`;
|
const path = `/rest/users/user/${user.id}/reset_password/`;
|
||||||
await this.httpService.post(path, { password: password });
|
await this.httpService.post(path, { password: password });
|
||||||
|
await this.update({default_password: password}, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user