diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js index c25987e6c36..8149602f329 100644 --- a/app/assets/javascripts/filterable_list.js +++ b/app/assets/javascripts/filterable_list.js @@ -30,8 +30,8 @@ export default class FilterableList { } filterResults(url, data) { - const endpoint = url || this.filterForm.getAttribute('action'); - const additionalData = data || $(this.filterForm).serialize(); + const endpoint = url || this.filterForm.getAttribute('action'); + const additionalData = data || $(this.filterForm).serialize(); $(this.listHolderElement).fadeTo(250, 0.5); diff --git a/app/assets/javascripts/groups/components/group_folder.vue b/app/assets/javascripts/groups/components/group_folder.vue index 75b20111ca1..abe3b90f663 100644 --- a/app/assets/javascripts/groups/components/group_folder.vue +++ b/app/assets/javascripts/groups/components/group_folder.vue @@ -2,7 +2,7 @@ export default { props: { groups: { - type: Array, + type: Object, required: true, }, }, diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue index 2c0f9e73e34..07fa648b237 100644 --- a/app/assets/javascripts/groups/components/group_item.vue +++ b/app/assets/javascripts/groups/components/group_item.vue @@ -10,10 +10,6 @@ export default { }, methods: { toggleSubGroups() { - if (!this.group.hasSubgroups) { - return; - } - eventHub.$emit('toggleSubGroups', this.group); }, }, @@ -21,8 +17,11 @@ export default { diff --git a/app/assets/javascripts/groups/components/groups.vue b/app/assets/javascripts/groups/components/groups.vue index 2ee7ec96dfe..45cc483b06e 100644 --- a/app/assets/javascripts/groups/components/groups.vue +++ b/app/assets/javascripts/groups/components/groups.vue @@ -2,7 +2,7 @@ export default { props: { groups: { - type: Array, + type: Object, required: true, }, }, diff --git a/app/assets/javascripts/groups/groups_filterable_list.js b/app/assets/javascripts/groups/groups_filterable_list.js index f8ba11caccc..be89955c101 100644 --- a/app/assets/javascripts/groups/groups_filterable_list.js +++ b/app/assets/javascripts/groups/groups_filterable_list.js @@ -1,6 +1,5 @@ import FilterableList from '~/filterable_list'; - export default class GroupFilterableList extends FilterableList { constructor(form, filter, holder, store) { super(form, filter, holder); diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js index d136e64850f..5b94f7b762d 100644 --- a/app/assets/javascripts/groups/index.js +++ b/app/assets/javascripts/groups/index.js @@ -31,19 +31,24 @@ $(() => { }; }, methods: { - fetchGroups() { - service.getGroups() + fetchGroups(parentGroup) { + let parentId = null; + + if (parentGroup) { + parentId = parentGroup.id; + } + + service.getGroups(parentId) .then((response) => { - store.setGroups(response.json()); + store.setGroups(response.json(), parentGroup); }) .catch(() => { // TODO: Handler error }); }, - toggleSubGroups(group) { - GroupsStore.toggleSubGroups(group); - - this.fetchGroups(); + toggleSubGroups(parentGroup = null) { + GroupsStore.toggleSubGroups(parentGroup); + this.fetchGroups(parentGroup); }, }, created() { diff --git a/app/assets/javascripts/groups/services/groups_service.js b/app/assets/javascripts/groups/services/groups_service.js index 4c5ce87f396..d92e93d5fa9 100644 --- a/app/assets/javascripts/groups/services/groups_service.js +++ b/app/assets/javascripts/groups/services/groups_service.js @@ -8,7 +8,15 @@ export default class GroupsService { this.groups = Vue.resource(endpoint); } - getGroups() { - return this.groups.get(); + getGroups(parentId) { + let data = {}; + + if (parentId) { + data = { + parent_id: parentId, + }; + } + + return this.groups.get(data); } } diff --git a/app/assets/javascripts/groups/stores/groups_store.js b/app/assets/javascripts/groups/stores/groups_store.js index c0ef4f776e0..bf43441bd71 100644 --- a/app/assets/javascripts/groups/stores/groups_store.js +++ b/app/assets/javascripts/groups/stores/groups_store.js @@ -1,15 +1,48 @@ export default class GroupsStore { constructor() { this.state = {}; - this.state.groups = []; + this.state.groups = {}; return this; } - setGroups(groups) { - this.state.groups = this.decorateGroups(groups); + setGroups(rawGroups, parent = null) { + const parentGroup = parent; - return groups; + if (parentGroup) { + parentGroup.subGroups = this.buildTree(rawGroups); + } else { + this.state.groups = this.buildTree(rawGroups); + } + + return rawGroups; + } + + buildTree(rawGroups) { + const groups = this.decorateGroups(rawGroups); + const tree = {}; + const mappedGroups = {}; + + // Map groups to an object + for (let i = 0, len = groups.length; i < len; i += 1) { + const group = groups[i]; + mappedGroups[group.id] = group; + mappedGroups[group.id].subGroups = {}; + } + + Object.keys(mappedGroups).forEach((key) => { + const currentGroup = mappedGroups[key]; + // If the group is not at the root level, add it to its parent array of subGroups. + if (currentGroup.parentId) { + mappedGroups[currentGroup.parentId].subGroups[currentGroup.id] = currentGroup; + mappedGroups[currentGroup.parentId].isOpen = true; // Expand group if it has subgroups + } else { + // If the group is at the root level, add it to first level elements array. + tree[currentGroup.id] = currentGroup; + } + }); + + return tree; } decorateGroups(rawGroups) { @@ -19,12 +52,14 @@ export default class GroupsStore { static decorateGroup(rawGroup) { return { - fullName: rawGroup.name, + id: rawGroup.id, + fullName: rawGroup.full_name, description: rawGroup.description, webUrl: rawGroup.web_url, - parentId: rawGroup.parentId, - hasSubgroups: !!rawGroup.parent_id, + parentId: rawGroup.parent_id, + expandable: true, isOpen: false, + subGroups: {}, }; } diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 72d73b89a2a..8c7e5591d79 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -111,3 +111,9 @@ height: 50px; } } + + +.list-group .list-group { + margin-top: 10px; + margin-bottom: 0; +}