diff --git a/client/src/app/core/repositories/motions/motion-repository.service.ts b/client/src/app/core/repositories/motions/motion-repository.service.ts index 3874fb4f6..15b52bd23 100644 --- a/client/src/app/core/repositories/motions/motion-repository.service.ts +++ b/client/src/app/core/repositories/motions/motion-repository.service.ts @@ -319,6 +319,19 @@ export class MotionRepositoryService extends BaseRepository 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 { + 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 * diff --git a/client/src/app/core/ui-services/tree.service.ts b/client/src/app/core/ui-services/tree.service.ts index 47b7656b8..9e1b98465 100644 --- a/client/src/app/core/ui-services/tree.service.ts +++ b/client/src/app/core/ui-services/tree.service.ts @@ -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(branch: OSTreeNode, 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(tree: OSTreeNode[], 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(tree: OSTreeNode[], 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; + } } diff --git a/client/src/app/site/motions/components/motion-list/motion-list.component.html b/client/src/app/site/motions/components/motion-list/motion-list.component.html index b68c56e08..00345fe4f 100644 --- a/client/src/app/site/motions/components/motion-list/motion-list.component.html +++ b/client/src/app/site/motions/components/motion-list/motion-list.component.html @@ -248,10 +248,13 @@ Add/remove tags +
diff --git a/client/src/app/site/motions/services/motion-multiselect.service.ts b/client/src/app/site/motions/services/motion-multiselect.service.ts index 11df04da3..f62042c2b 100644 --- a/client/src/app/site/motions/services/motion-multiselect.service.ts +++ b/client/src/app/site/motions/services/motion-multiselect.service.ts @@ -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 { + 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); + } + } + } }