diff --git a/awx/ui/client/legacy/styles/lists.less b/awx/ui/client/legacy/styles/lists.less index 84514339de..4ca8c0a12a 100644 --- a/awx/ui/client/legacy/styles/lists.less +++ b/awx/ui/client/legacy/styles/lists.less @@ -219,7 +219,7 @@ table, tbody { } .List-actionHolder--leftAlign { - margin-left: 52%; + margin-left: 50%; justify-content: flex-start; button { height: 34px; @@ -230,6 +230,15 @@ table, tbody { align-items: center; display: flex; margin-bottom: -34px; + + .List-buttonDefault { + margin-left: 20px; + } + + .List-toggleButton { + height: 30px; + line-height: 14px; + } } .List-auxAction { @@ -272,6 +281,10 @@ table, tbody { opacity: 0.65; } +.List-actionButton { + margin-left: 20px; +} + .List-searchDropdown { border-top-left-radius: 5px!important; border-bottom-left-radius: 5px!important; diff --git a/awx/ui/client/src/inventories-hosts/inventories/main.js b/awx/ui/client/src/inventories-hosts/inventories/main.js index 923d3ef1fe..a3160d7b1f 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/main.js +++ b/awx/ui/client/src/inventories-hosts/inventories/main.js @@ -3,6 +3,7 @@ * * All Rights Reserved *************************************************/ +import { N_ } from '../../i18n'; import adhoc from './adhoc/main'; import group from './related/groups/main'; @@ -294,6 +295,78 @@ angular.module('inventory', [ let relatedHostCompletedJobs = _.cloneDeep(hostCompletedJobsRoute); relatedHostCompletedJobs.name = 'inventories.edit.hosts.edit.completed_jobs'; + let inventoryRootGroupsList = _.cloneDeep(inventoryGroupsList); + inventoryRootGroupsList.name = "inventories.edit.rootGroups"; + inventoryRootGroupsList.url = "/root_groups?{group_search:queryset}", + inventoryRootGroupsList.ncyBreadcrumb.label = N_("ROOT GROUPS");// jshint ignore:line + inventoryRootGroupsList.resolve.listDefinition = ['GroupList', (list) => { + const rootGroupList = _.cloneDeep(list); + rootGroupList.basePath = 'api/v2/inventories/{{$stateParams.inventory_id}}/root_groups/'; + rootGroupList.fields.name.uiSref = "inventories.edit.rootGroups.edit({group_id:group.id})"; + return rootGroupList; + }]; + + let inventoryRootGroupsAdd = _.cloneDeep(inventoryGroupsAdd); + inventoryRootGroupsAdd.name = "inventories.edit.rootGroups.add"; + inventoryRootGroupsAdd.ncyBreadcrumb.parent = "inventories.edit.rootGroups"; + + let inventoryRootGroupsEdit = _.cloneDeep(inventoryGroupsEdit); + inventoryRootGroupsEdit.name = "inventories.edit.rootGroups.edit"; + inventoryRootGroupsEdit.ncyBreadcrumb.parent = "inventories.edit.rootGroups"; + inventoryRootGroupsEdit.views = { + 'groupForm@inventories': { + templateProvider: function(GenerateForm, GroupForm) { + let form = _.cloneDeep(GroupForm); + form.activeEditState = 'inventories.edit.rootGroups.edit'; + form.detailsClick = "$state.go('inventories.edit.rootGroups.edit')"; + form.parent = 'inventories.edit.rootGroups'; + form.related.nested_groups.ngClick = "$state.go('inventories.edit.rootGroups.edit.nested_groups')"; + form.related.nested_hosts.ngClick = "$state.go('inventories.edit.rootGroups.edit.nested_hosts')"; + + return GenerateForm.buildHTML(form, { + mode: 'edit', + related: false + }); + }, + controller: 'GroupEditController' + } + }; + inventoryGroupsEdit.views = { + 'groupForm@inventories': { + templateProvider: function(GenerateForm, GroupForm) { + let form = GroupForm; + + return GenerateForm.buildHTML(form, { + mode: 'edit', + related: false + }); + }, + controller: 'GroupEditController' + } + }; + + let rootGroupNestedGroupsRoute = _.cloneDeep(groupNestedGroupsRoute); + rootGroupNestedGroupsRoute.name = 'inventories.edit.rootGroups.edit.nested_groups'; + rootGroupNestedGroupsRoute.ncyBreadcrumb.parent = "inventories.edit.rootGroups.edit"; + + let rootNestedGroupsAdd = _.cloneDeep(nestedGroupsAdd); + rootNestedGroupsAdd.name = "inventories.edit.rootGroups.edit.nested_groups.add"; + rootNestedGroupsAdd.ncyBreadcrumb.parent = "inventories.edit.groups.edit.nested_groups"; + + let rootGroupNestedGroupsAssociateRoute = _.cloneDeep(groupNestedGroupsAssociateRoute); + rootGroupNestedGroupsAssociateRoute.name = 'inventories.edit.rootGroups.edit.nested_groups.associate'; + + let rootGroupNestedHostsRoute = _.cloneDeep(nestedHostsRoute); + rootGroupNestedHostsRoute.name = 'inventories.edit.rootGroups.edit.nested_hosts'; + rootGroupNestedHostsRoute.ncyBreadcrumb.parent = "inventories.edit.rootGroups.edit"; + + let rootNestedHostsAdd = _.cloneDeep(nestedHostsAddRoute); + rootNestedHostsAdd.name = "inventories.edit.rootGroups.edit.nested_hosts.add"; + rootNestedHostsAdd.ncyBreadcrumb.parent = "inventories.edit.rootGroups.edit.nested_hosts"; + + let rootGroupNestedHostsAssociateRoute = _.cloneDeep(nestedHostsAssociateRoute); + rootGroupNestedHostsAssociateRoute.name = 'inventories.edit.rootGroups.edit.nested_hosts.associate'; + return Promise.all([ standardInventoryAdd, standardInventoryEdit, @@ -315,10 +388,16 @@ angular.module('inventory', [ stateExtender.buildDefinition(relatedHostsAnsibleFacts), stateExtender.buildDefinition(relatedHostsInsights), stateExtender.buildDefinition(inventoryGroupsList), + stateExtender.buildDefinition(inventoryRootGroupsList), stateExtender.buildDefinition(inventoryGroupsAdd), + stateExtender.buildDefinition(rootNestedGroupsAdd), + stateExtender.buildDefinition(inventoryRootGroupsAdd), stateExtender.buildDefinition(inventoryGroupsEdit), + stateExtender.buildDefinition(inventoryRootGroupsEdit), stateExtender.buildDefinition(groupNestedGroupsRoute), + stateExtender.buildDefinition(rootGroupNestedGroupsRoute), stateExtender.buildDefinition(nestedHostsRoute), + stateExtender.buildDefinition(rootGroupNestedHostsRoute), stateExtender.buildDefinition(inventoryHosts), stateExtender.buildDefinition(smartInventoryHosts), stateExtender.buildDefinition(inventoryHostsAdd), @@ -338,10 +417,13 @@ angular.module('inventory', [ stateExtender.buildDefinition(addSourceProject), stateExtender.buildDefinition(editSourceProject), stateExtender.buildDefinition(groupNestedGroupsAssociateRoute), + stateExtender.buildDefinition(rootGroupNestedGroupsAssociateRoute), stateExtender.buildDefinition(hostNestedGroupsAssociateRoute), stateExtender.buildDefinition(nestedHostsAssociateRoute), + stateExtender.buildDefinition(rootGroupNestedHostsAssociateRoute), stateExtender.buildDefinition(nestedGroupsAdd), stateExtender.buildDefinition(nestedHostsAddRoute), + stateExtender.buildDefinition(rootNestedHostsAdd), stateExtender.buildDefinition(relatedHostCompletedJobs) ]) }; diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/groups/edit/groups-edit.route.js b/awx/ui/client/src/inventories-hosts/inventories/related/groups/edit/groups-edit.route.js index 0fbdd763c7..6d1a670168 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/groups/edit/groups-edit.route.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/groups/edit/groups-edit.route.js @@ -5,18 +5,6 @@ export default { parent: "inventories.edit.groups", label: "{{breadcrumb.group_name}}" }, - views: { - 'groupForm@inventories': { - templateProvider: function(GenerateForm, GroupForm) { - let form = GroupForm; - return GenerateForm.buildHTML(form, { - mode: 'edit', - related: false - }); - }, - controller: 'GroupEditController' - } - }, resolve: { groupData: ['$stateParams', 'GroupsService', function($stateParams, GroupsService) { return GroupsService.get({ id: $stateParams.group_id }).then(response => response.data.results[0]); diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/groups/groups.form.js b/awx/ui/client/src/inventories-hosts/inventories/related/groups/groups.form.js index 1e4c055741..74448958b0 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/groups/groups.form.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/groups/groups.form.js @@ -98,7 +98,6 @@ function(i18n){ title: i18n._('Hosts'), iterator: 'nested_hosts' }, - } }; }]; diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/groups/groups.list.js b/awx/ui/client/src/inventories-hosts/inventories/related/groups/groups.list.js index cc316a16aa..895bfe5687 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/groups/groups.list.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/groups/groups.list.js @@ -52,6 +52,22 @@ actionClass: 'btn List-buttonDefault', buttonContent: i18n._('REFRESH') }, + groupsToggle: { + mode: 'all', + type: 'toggle', + buttons: [ + { + text: i18n._('ALL GROUPS'), + ngClick: "$state.go('inventories.edit.groups')", + ngClass: "{'btn-primary': $state.includes('inventories.edit.groups'), 'Button-primary--hollow': $state.includes('inventories.edit.rootGroups')}" + }, + { + text: i18n._('ROOT GROUPS'), + ngClick: "$state.go('inventories.edit.rootGroups')", + ngClass: "{'btn-primary': $state.includes('inventories.edit.rootGroups'), 'Button-primary--hollow': $state.includes('inventories.edit.groups')}" + } + ] + }, launch: { mode: 'all', ngDisabled: '!groupsSelected', diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/groups/list/groups-list.controller.js b/awx/ui/client/src/inventories-hosts/inventories/related/groups/list/groups-list.controller.js index daf0ab3e81..f375e757eb 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/groups/list/groups-list.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/groups/list/groups-list.controller.js @@ -4,16 +4,16 @@ * All Rights Reserved *************************************************/ export default - ['$scope', '$rootScope', '$state', '$stateParams', 'GroupList', 'InventoryUpdate', - 'GroupsService', 'CancelSourceUpdate', 'rbacUiControlService', 'GetBasePath', - 'GetHostsStatusMsg', 'Dataset', 'Find', 'QuerySet', 'inventoryData', 'canAdd', + ['$scope', '$state', '$stateParams', 'listDefinition', 'InventoryUpdate', + 'GroupsService', 'CancelSourceUpdate', + 'GetHostsStatusMsg', 'Dataset', 'inventoryData', 'canAdd', 'InventoryHostsStrings', '$transitions', - function($scope, $rootScope, $state, $stateParams, GroupList, InventoryUpdate, - GroupsService, CancelSourceUpdate, rbacUiControlService, GetBasePath, - GetHostsStatusMsg, Dataset, Find, qs, inventoryData, canAdd, + function($scope, $state, $stateParams, listDefinition, InventoryUpdate, + GroupsService, CancelSourceUpdate, + GetHostsStatusMsg, Dataset, inventoryData, canAdd, InventoryHostsStrings, $transitions){ - let list = GroupList; + let list = listDefinition; init(); @@ -85,10 +85,18 @@ } $scope.createGroup = function(){ - $state.go('inventories.edit.groups.add'); + if ($state.includes('inventories.edit.groups')) { + $state.go('inventories.edit.groups.add'); + } else if ($state.includes('inventories.edit.rootGroups')) { + $state.go('inventories.edit.rootGroups.add'); + } }; $scope.editGroup = function(id){ - $state.go('inventories.edit.groups.edit', {group_id: id}); + if ($state.includes('inventories.edit.groups')) { + $state.go('inventories.edit.groups.edit', {group_id: id}); + } else if ($state.includes('inventories.edit.rootGroups')) { + $state.go('inventories.edit.rootGroups.edit', {group_id: id}); + } }; $scope.goToGroupGroups = function(id){ $state.go('inventories.edit.groups.edit.nested_groups', {group_id: id}); diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/groups/list/groups-list.route.js b/awx/ui/client/src/inventories-hosts/inventories/related/groups/list/groups-list.route.js index c16e9ba2a1..2ad7a90a0a 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/groups/list/groups-list.route.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/groups/list/groups-list.route.js @@ -5,7 +5,8 @@ export default { name: "inventories.edit.groups", url: "/groups?{group_search:queryset}", resolve: { - Dataset: ['GroupList', 'QuerySet', '$stateParams', 'GetBasePath', '$interpolate', '$rootScope', + listDefinition: ['GroupList', (list) => list], + Dataset: ['listDefinition', 'QuerySet', '$stateParams', 'GetBasePath', '$interpolate', '$rootScope', (list, qs, $stateParams, GetBasePath, $interpolate, $rootScope) => { // allow related list definitions to use interpolated $rootScope / $stateParams in basePath field let path, interpolator; @@ -43,12 +44,12 @@ export default { }, ncyBreadcrumb: { parent: "inventories.edit", - label: N_("GROUPS") + label: N_("ALL GROUPS") }, views: { 'related': { - templateProvider: function(GroupList, generateList, $templateRequest, $stateParams, GetBasePath) { - let list = _.cloneDeep(GroupList); + templateProvider: function(listDefinition, generateList, $templateRequest, $stateParams, GetBasePath) { + let list = _.cloneDeep(listDefinition); if($stateParams && $stateParams.group) { list.basePath = GetBasePath('groups') + _.last($stateParams.group) + '/children'; } diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups-associate.route.js b/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups-associate.route.js index 6f8f86e860..0df6fdc481 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups-associate.route.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups-associate.route.js @@ -6,7 +6,7 @@ export default { skip:true }, views: { - 'modal@inventories.edit.groups.edit': { + 'modal@^.^': { templateProvider: function() { return ``; }, diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups-list.controller.js b/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups-list.controller.js index 88d8eb04ed..65961998d6 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups-list.controller.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-groups/group-nested-groups-list.controller.js @@ -127,7 +127,11 @@ }; $scope.editGroup = function(id){ - $state.go('inventories.edit.groups.edit', {group_id: id}); + if ($state.includes('inventories.edit.groups')) { + $state.go('inventories.edit.groups.edit', {group_id: id}); + } else if ($state.includes('inventories.edit.rootGroups')) { + $state.go('inventories.edit.rootGroups.edit', {group_id: id}); + } }; $scope.goToGroupGroups = function(id){ diff --git a/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-hosts/group-nested-hosts-associate.route.js b/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-hosts/group-nested-hosts-associate.route.js index 1d1ad65388..220a5a4285 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-hosts/group-nested-hosts-associate.route.js +++ b/awx/ui/client/src/inventories-hosts/inventories/related/groups/related/nested-hosts/group-nested-hosts-associate.route.js @@ -6,7 +6,7 @@ export default { skip:true }, views: { - 'modal@inventories.edit.groups.edit': { + 'modal@^.^': { templateProvider: function() { return ``; }, diff --git a/awx/ui/client/src/inventories-hosts/inventories/standard-inventory/inventory.form.js b/awx/ui/client/src/inventories-hosts/inventories/standard-inventory/inventory.form.js index d78dae4e49..7e8083bf48 100644 --- a/awx/ui/client/src/inventories-hosts/inventories/standard-inventory/inventory.form.js +++ b/awx/ui/client/src/inventories-hosts/inventories/standard-inventory/inventory.form.js @@ -149,6 +149,7 @@ function(i18n) { include: "GroupList", title: i18n._('Groups'), iterator: 'group', + tabSelected: `$state.includes('inventories.edit.groups') || $state.includes('inventories.edit.rootGroups')`, skipGenerator: true }, hosts: { diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index 0e484f5f00..c1b4d7f35f 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -1469,7 +1469,13 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat `aw-tip-placement="${collection.dataPlacement}" ` + `data-tip-watch="${collection.dataTipWatch}" `; } - let relatedTabSelected = this.form.activeEditState ? `$state.includes('${this.form.activeEditState}.${itm}') || $state.includes('${this.form.stateTree}.edit.${itm}')` : `$state.includes('${this.form.stateTree}.edit.${itm}')`; + let relatedTabSelected; + if (this.form.related[itm].tabSelected) { + relatedTabSelected = this.form.related[itm].tabSelected; + } else { + relatedTabSelected = this.form.activeEditState ? `$state.includes('${this.form.activeEditState}.${itm}') || $state.includes('${this.form.stateTree}.edit.${itm}')` : `$state.includes('${this.form.stateTree}.edit.${itm}')`; + } + html += `ng-class="{'is-selected' : ${relatedTabSelected}` ; if(this.form.related[itm].disabled){ html += `, 'Form-tab--disabled' : ${this.form.related[itm].disabled }`; diff --git a/awx/ui/client/src/shared/list-generator/list-actions.partial.html b/awx/ui/client/src/shared/list-generator/list-actions.partial.html index d2552b7b2c..88b2ab5b01 100644 --- a/awx/ui/client/src/shared/list-generator/list-actions.partial.html +++ b/awx/ui/client/src/shared/list-generator/list-actions.partial.html @@ -1,6 +1,9 @@ - + +
+ +