Merge pull request #4253 from MaximilianKrambach/sortMotionCallList

bulk move motions in call list
This commit is contained in:
Maximilian Krambach 2019-02-11 13:51:44 +01:00 committed by GitHub
commit 7dd086eacf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 2 deletions

View File

@ -319,6 +319,19 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
await this.httpService.post(url, data);
}
/**
* Sends the changed nodes to the server, with only the top nodes being submitted.
*
* @param data The reordered data from the sorting, as list of ViewMotions
* @param parent a parent id
*/
public async sortMotionBranches(data: ViewMotion[], parent?: number): Promise<void> {
const url = '/rest/motions/motion/sort/';
const nodes = data.map(motion => ({ id: motion.id }));
const params = parent ? { nodes: nodes, parent_id: parent } : { nodes: nodes };
await this.httpService.post(url, params);
}
/**
* Supports the motion
*

View File

@ -131,4 +131,61 @@ export class TreeService {
const tree = this.makeTree(items, weightKey, parentIdKey);
return this.traverseTree(tree);
}
/**
* Reduce a list of items to nodes independent from each other in a given
* branch of a tree
*
* @param branch the tree to traverse
* @param items the items to check
* @returns the selection of items that belong to different branches
*/
private getTopItemsFromBranch<T extends Identifiable & Displayable>(branch: OSTreeNode<T>, items: T[]): T[] {
const item = items.find(i => branch.item.id === i.id);
if (item) {
return [item];
} else if (!branch.children) {
return [];
} else {
return [].concat(...branch.children.map(child => this.getTopItemsFromBranch(child, items)));
}
}
/**
* Reduce a list of items to nodes independent from each other in a given tree
*
* @param tree the tree to traverse
* @param items the items to check
* @returns the selection of items that belong to different branches
*/
public getTopItemsFromTree<T extends Identifiable & Displayable>(tree: OSTreeNode<T>[], items: T[]): T[] {
let results: T[] = [];
tree.forEach(branch => {
const i = this.getTopItemsFromBranch(branch, items);
if (i.length) {
results = results.concat(i);
}
});
return results;
}
/**
* Return all items not being hierarchically dependant on the items in the input arrray
*
* @param tree
* @param items
* @returns all items that are neither in the input nor dependants of items in the input
*/
public getTreeWithoutSelection<T extends Identifiable & Displayable>(tree: OSTreeNode<T>[], items: T[]): T[] {
let result: T[] = [];
tree.forEach(branch => {
if (!items.find(i => i.id === branch.item.id)) {
result.push(branch.item);
if (branch.children) {
result = result.concat(this.getTreeWithoutSelection(branch.children, items));
}
}
});
return result;
}
}

View File

@ -248,10 +248,13 @@
<span translate>Add/remove tags</span>
</button>
<button mat-menu-item (click)="multiselectWrapper(multiselectService.moveToItem(selectedRows))">
<!-- TODO: Not implemented yet -->
<mat-icon>sort</mat-icon>
<span translate>Move to agenda item</span>
</button>
<button mat-menu-item (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')">
<mat-divider></mat-divider>

View File

@ -15,6 +15,7 @@ import { ItemRepositoryService } from 'app/core/repositories/agenda/item-reposit
import { Displayable } from 'app/site/base/displayable';
import { Identifiable } from 'app/shared/models/base/identifiable';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
import { TreeService } from 'app/core/ui-services/tree.service';
/**
* Contains all multiselect actions for the motion list view.
@ -37,6 +38,7 @@ export class MotionMultiselectService {
* @param agendaRepo
* @param motionBlockRepo
* @param httpService
* @param treeService
*/
public constructor(
private repo: MotionRepositoryService,
@ -49,7 +51,8 @@ export class MotionMultiselectService {
private tagRepo: TagRepositoryService,
private agendaRepo: ItemRepositoryService,
private motionBlockRepo: MotionBlockRepositoryService,
private httpService: HttpService
private httpService: HttpService,
private treeService: TreeService
) {}
/**
@ -247,4 +250,37 @@ export class MotionMultiselectService {
}
}
}
/**
* Triggers the selected motions to be moved in the call-list (sort_parent, weight)
* as children or as following after a selected motion.
*
* @param motions The motions to move
*/
public async bulkMoveItems(motions: ViewMotion[]): Promise<void> {
const title = this.translate.instant('This will assign the selected motions as belonging to:');
const options = ['Set as parent', 'Insert after'];
const allMotions = this.repo.getViewModelList();
const tree = this.treeService.makeTree(allMotions, 'weight', 'sort_parent_id');
const itemsToMove = this.treeService.getTopItemsFromTree(tree, motions);
const selectableItems = this.treeService.getTreeWithoutSelection(tree, motions);
const selectedChoice = await this.choiceService.open(title, selectableItems, false, options);
if (selectedChoice) {
if (selectedChoice.action === options[0]) {
// set choice as parent
this.repo.sortMotionBranches(itemsToMove, selectedChoice.items as number);
} else if (selectedChoice.action === options[1]) {
// insert after chosen
const olderSibling = this.repo.getViewModel(selectedChoice.items as number);
const parentId = olderSibling ? olderSibling.sort_parent_id : null;
const siblings = this.repo.getViewModelList().filter(motion => motion.sort_parent_id === parentId);
const idx = siblings.findIndex(sib => sib.id === olderSibling.id);
const before = siblings.slice(0, idx + 1);
const after = siblings.slice(idx + 1);
const sum = [].concat(before, itemsToMove, after);
this.repo.sortMotionBranches(sum, parentId);
}
}
}
}