diff --git a/awx/ui/static/js/controllers/Home.js b/awx/ui/static/js/controllers/Home.js index 9a422b1159..576f6c0660 100644 --- a/awx/ui/static/js/controllers/Home.js +++ b/awx/ui/static/js/controllers/Home.js @@ -95,7 +95,7 @@ Home.$inject=['$scope', '$compile', '$routeParams', '$rootScope', '$location', ' function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, - GetBasePath, SearchInit, PaginateInit, FormatDate, HostsStatusMsg, UpdateStatusMsg, ViewUpdateStatus, Stream) { + GetBasePath, SearchInit, PaginateInit, FormatDate, HostsStatusMsg, GetSyncStatusMsg, ViewUpdateStatus, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -129,7 +129,7 @@ function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, Proce group_id: scope.home_groups[i].id }); - update_status = UpdateStatusMsg({ status: scope.home_groups[i].summary_fields.inventory_source.status }); + update_status = GetSyncStatusMsg({ status: scope.home_groups[i].summary_fields.inventory_source.status }); scope.home_groups[i].failed_hosts_tip = msg['tooltip']; scope.home_groups[i].failed_hosts_link = msg['url']; @@ -210,7 +210,7 @@ function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, Proce } HomeGroups.$inject = [ '$location', '$routeParams', 'HomeGroupList', 'GenerateList', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', - 'ClearScope', 'GetBasePath', 'SearchInit', 'PaginateInit', 'FormatDate', 'HostsStatusMsg', 'UpdateStatusMsg', 'ViewUpdateStatus', 'Stream' + 'ClearScope', 'GetBasePath', 'SearchInit', 'PaginateInit', 'FormatDate', 'HostsStatusMsg', 'GetSyncStatusMsg', 'ViewUpdateStatus', 'Stream' ]; diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index ff08961db8..6022108e47 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -317,7 +317,8 @@ InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$lo function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateList, ClearScope, InventoryGroups, InventoryHosts, BuildTree, Wait, - UpdateStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, Breadcrumbs, LoadBreadCrumbs) + GetSyncStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, Breadcrumbs, LoadBreadCrumbs, Empty, Rest, + ProcessErrors, InventoryUpdate, Alert, ToggleChildren) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -335,12 +336,15 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis } $scope.removeSearchTreeReady = $scope.$on('searchTreeReady', function(e, inventory_name, groups) { // After the tree data loads, generate the groups list + // Add breadcrumbs var e = angular.element(document.getElementById('breadcrumbs')); e.html(Breadcrumbs({ list: list, mode: 'edit' })); $compile(e)($scope); + // Add groups generator.inject(list, { mode: 'edit', id: 'groups-container', breadCrumbs: false, searchSize: 'col-lg-5 col-md-5 col-sm-5' }); $scope.groups = groups; $scope.inventory_name = inventory_name; + // Add hosts InjectHosts({ scope: $scope, inventory_id: $scope.inventory_id }); Wait('stop'); }); @@ -361,18 +365,62 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis } $scope.createGroup = function() { - GroupsAdd({ scope: $scope, inventory_id: $scope.inventory_id, group_id: null }); + GroupsAdd({ scope: $scope, inventory_id: $scope.inventory_id, group_id: $scope.selected_group_id }); } $scope.editGroup = function(group_id) { GroupsEdit({ scope: $scope, inventory_id: $scope.inventory_id, group_id: group_id }); } + // Launch inventory sync + $scope.updateGroup = function(id) { + for (var i=0; i < $scope.groups.length; i++) { + if ($scope.groups[i].id == id) { + if (Empty($scope.groups[i].source)) { + // if no source, do nothing. + } + else if ($scope.groups[i].status == 'updating') { + Alert('Update in Progress', 'The inventory update process is currently running for group ' + + $scope.groups[i].name + '. Use the Refresh button to monitor the status.', 'alert-info'); + } + else { + Wait('start'); + Rest.setUrl($scope.groups[i].related.inventory_source); + Rest.get() + .success( function(data, status, headers, config) { + InventoryUpdate({ + scope: $scope, + url: data.related.update, + group_name: data.summary_fields.group.name, + group_source: data.source + }); + }) + .error( function(data, status, headers, config) { + Wait('stop'); + ProcessErrors(scope, data, status, form, + { hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' + $scope.groups[i].related.inventory_source + + ' POST returned status: ' + status }); + }); + } + break; + } + } + } + + // Expand/collapse nodes + $scope.toggle = function(id) { + ToggleChildren({ + scope: $scope, + list: list, + id: id, + }); + } + BuildTree({ scope: $scope, inventory_id: $scope.inventory_id }); } InventoriesEdit.$inject = [ '$scope', '$location', '$routeParams', '$compile', 'GenerateList', 'ClearScope', 'InventoryGroups', 'InventoryHosts', - 'BuildTree', 'Wait', 'UpdateStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'Breadcrumbs', - 'LoadBreadCrumbs' + 'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'Breadcrumbs', + 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren' ]; diff --git a/awx/ui/static/js/controllers/JobEvents.js b/awx/ui/static/js/controllers/JobEvents.js index 037185a08d..7a60e28863 100644 --- a/awx/ui/static/js/controllers/JobEvents.js +++ b/awx/ui/static/js/controllers/JobEvents.js @@ -132,7 +132,7 @@ function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest, set[i].event_display = set[i].event_display.replace(/^\u00a0*/g,''); if (set[i].event_level < 3 ) { set[i]['ngclick'] = "toggleChildren(" + set[i].id + ", \"" + set[i].related.children + "\")"; - set[i]['ngicon'] = 'icon-collapse-alt'; + set[i]['ngicon'] = 'fa fa-minus-square-o'; set[i]['class'] = 'parentNode'; } else { diff --git a/awx/ui/static/js/helpers/Children.js b/awx/ui/static/js/helpers/Children.js index de004ea405..679ebe69f9 100644 --- a/awx/ui/static/js/helpers/Children.js +++ b/awx/ui/static/js/helpers/Children.js @@ -17,11 +17,10 @@ angular.module('ChildrenHelper', ['RestServices', 'Utilities']) var scope = params.scope; var list = params.list; var id = params.id; - var children = params.children; var set = scope[list.name]; // set is now a pointer to scope[list.name] function expand(node) { - set[node]['ngicon'] = 'icon-collapse-alt'; + set[node]['ngicon'] = 'fa fa-minus-square-o'; for (var i = node + 1; i < set.length; i++) { if (set[i].parent == set[node].id) { set[i]['show'] = true; @@ -33,12 +32,12 @@ angular.module('ChildrenHelper', ['RestServices', 'Utilities']) } function collapse(node) { - set[node]['ngicon'] = 'icon-expand-alt'; + set[node]['ngicon'] = 'fa fa-plus-square-o'; for (var i = node + 1; i < set.length; i++) { if (set[i].parent == set[node].id) { set[i]['show'] = false; if (set[i]['related']['children']) { - collapse(i); + collapse(i); } } } @@ -54,7 +53,7 @@ angular.module('ChildrenHelper', ['RestServices', 'Utilities']) } } // Expand or collapse children based on clicked element's icon - if (set[clicked]['ngicon'] == 'icon-expand-alt') { + if (set[clicked]['ngicon'] == 'fa fa-plus-square-o') { // Expand: lookup and display children expand(clicked); } diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index 94cf2e127b..648c443e9f 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -130,43 +130,45 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } }]) - .factory('HostsStatusMsg', [ function() { + .factory('GetHostsStatusMsg', [ function() { return function(params) { + var active_failures = params.active_failures; var total_hosts = params.total_hosts; var inventory_id = params.inventory_id; var group_id = params.group_id; - var tips, link, html_class; + + var tip, link, html_class; // Return values for use on host status indicator if (active_failures > 0) { - tip = "Contains " + active_failures + - [ (active_failures == 1) ? ' host' : ' hosts' ] + ' with failed jobs. Click to view the offending ' + - [ (active_failures == 1) ? ' host' : ' hosts' ] + '.'; - link = '/#/inventories/' + inventory_id + '/hosts?group=' + group_id + '&has_active_failures=true'; - html_class = 'true'; + tip = "Contains " + active_failures + + [ (active_failures == 1) ? ' host' : ' hosts' ] + ' with failed jobs. Click to view the offending ' + + [ (active_failures == 1) ? ' host' : ' hosts' ] + '.'; + link = '/#/inventories/' + inventory_id + '/hosts?group=' + group_id + '&has_active_failures=true'; + html_class = 'true'; } else { if (total_hosts == 0) { - // no hosts - tip = "There are no hosts in this group. It's a sad empty shell. Click to view the hosts page and add a host."; - link = '/#/inventories/' + inventory_id + '/hosts/?group=' + group_id; - html_class = 'na'; + // no hosts + tip = "There are no hosts in this group. It's a sad empty shell. Click to view the hosts page and add a host."; + link = '/#/inventories/' + inventory_id + '/hosts/?group=' + group_id; + html_class = 'na'; } else if (total_hosts == 1) { - // on host with 0 failures - tip = "The 1 host in this group is happy! It does not have a job failure. " + - " Click to view the host."; - link = '/#/inventories/' + inventory_id + '/hosts/?group=' + group_id; - html_class = 'false'; + // on host with 0 failures + tip = "The 1 host in this group is happy! It does not have a job failure. " + + " Click to view the host."; + link = '/#/inventories/' + inventory_id + '/hosts/?group=' + group_id; + html_class = 'false'; } else { - // many hosts with 0 failures - tip = "All " + total_hosts + " hosts in this group are happy! None of them have " + - " a recent job failure. Click to view the hosts."; - links = '/#/inventories/' + inventory_id + '/hosts/?group=' + group_id; - html_class = 'false'; + // many hosts with 0 failures + tip = "All " + total_hosts + " hosts in this group are happy! None of them have " + + " a recent job failure. Click to view the hosts."; + links = '/#/inventories/' + inventory_id + '/hosts/?group=' + group_id; + html_class = 'false'; } } @@ -175,39 +177,50 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } }]) - .factory('UpdateStatusMsg', [ function() { + .factory('GetSyncStatusMsg', [ function() { return function(params) { - var status = params.status; + var status = params.status; + + var launch_class = ''; + var launch_tip = 'Start sync process'; var stat, stat_class, status_tip; stat = status; - stat_class = stat; - + stat_class = 'icon-cloud-' + stat; + switch (status) { case 'never updated': stat = 'never'; - stat_class = 'never'; - status_tip = 'Inventory update has not been performed. Click the Update button to start it now.'; + stat_class = 'icon-cloud-na disabled'; + status_tip = 'Sync not performed. Click to start it now.'; break; case 'none': case '': + launch_class = 'btn-disabled', stat = 'n/a'; - stat_class = 'na'; - status_tip = 'Not configured for inventory update.'; + stat_class = 'icon-cloud-na disabled'; + status_tip = 'Group source not configured. Click to update.'; + launch_tip = status_tip; break; case 'failed': - status_tip = 'Inventory update completed with errors. Click to view process output.'; + status_tip = 'Failed with errors. Click to view log.'; break; case 'successful': - status_tip = 'Inventory update completed with no errors. Click to view process output.'; + status_tip = 'Success! Click to view log.'; break; case 'updating': - status_tip = 'Inventory update process running now.'; + status_tip = 'Running'; break; } - return { 'class': stat_class, tooltip: status_tip, status: stat } - + return { + 'class': stat_class, + tooltip: status_tip, + status: stat, + 'launch_class': launch_class, + 'launch_tip': launch_tip + } + } }]) @@ -317,10 +330,10 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' .factory('InventoryStatus', [ '$rootScope', '$routeParams', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'InventorySummary', 'GenerateList', 'ClearScope', 'SearchInit', 'PaginateInit', 'Refresh', 'InventoryUpdate', 'GroupsEdit', 'HelpDialog', - 'InventorySummaryHelp', 'ClickNode', 'HostsStatusMsg', 'UpdateStatusMsg', 'ViewUpdateStatus', 'Wait', + 'InventorySummaryHelp', 'ClickNode', 'GetHostsStatusMsg', 'GetSyncStatusMsg', 'ViewUpdateStatus', 'Wait', function($rootScope, $routeParams, Rest, Alert, ProcessErrors, GetBasePath, FormatDate, InventorySummary, GenerateList, ClearScope, SearchInit, PaginateInit, Refresh, InventoryUpdate, GroupsEdit, HelpDialog, InventorySummaryHelp, ClickNode, - HostsStatusMsg, UpdateStatusMsg, ViewUpdateStatus, Wait) { + GetHostsStatusMsg, GetSyncStatusMsg, ViewUpdateStatus, Wait) { return function(params) { //Build a summary of a given inventory @@ -345,14 +358,14 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' // Set values for Failed Hosts column scope.groups[i].failed_hosts = scope.groups[i].hosts_with_active_failures + ' / ' + scope.groups[i].total_hosts; - msg = HostsStatusMsg({ + msg = GetHostsStatusMsg({ active_failures: scope.groups[i].hosts_with_active_failures, total_hosts: scope.groups[i].total_hosts, inventory_id: scope['inventory_id'], group_id: scope['groups'][i]['id'] }); - update_status = UpdateStatusMsg({ status: scope.groups[i].summary_fields.inventory_source.status }); + update_status = GetSyncStatusMsg({ status: scope.groups[i].summary_fields.inventory_source.status }); scope.groups[i].failed_hosts_tip = msg['tooltip']; scope.groups[i].failed_hosts_link = msg['url']; diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index b25caa7e8e..e03583ec3d 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -189,7 +189,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H }]) - .factory('HostsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm', + .factory('HostsCreate', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors, GetBasePath, HostsReload, ParseTypeChange, Wait) { diff --git a/awx/ui/static/js/helpers/JobSubmission.js b/awx/ui/static/js/helpers/JobSubmission.js index b205c0a17a..2e919407e5 100644 --- a/awx/ui/static/js/helpers/JobSubmission.js +++ b/awx/ui/static/js/helpers/JobSubmission.js @@ -412,7 +412,6 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential var scope = params.scope; var inventory_id = params.inventory_id; var url = params.url; - var group_id = params.group_id; var group_name = params.group_name; var group_source = params.group_source; diff --git a/awx/ui/static/js/lists/InventoryGroups.js b/awx/ui/static/js/lists/InventoryGroups.js index f88692573d..4177403e9c 100644 --- a/awx/ui/static/js/lists/InventoryGroups.js +++ b/awx/ui/static/js/lists/InventoryGroups.js @@ -18,6 +18,7 @@ angular.module('InventoryGroupsDefinition', []) index: false, hover: true, hasChildren: true, + filterBy: '\{ show: true \}', 'class': 'table-condensed', fields: { @@ -26,40 +27,8 @@ angular.module('InventoryGroupsDefinition', []) key: true, ngClick: "\{\{ 'showHosts(' + group.id + ',' + group.group_id + ')' \}\}", ngClass: "group.selected_class", - hasChildren: true - }, - status: { - label: 'Sync Status', - ngClick: "viewUpdateStatus(\{\{ group.id \}\})", - searchType: 'select', - badgeIcon: "\{\{ 'fa icon-cloud-' + group.status_badge_class \}\}", - badgeToolTip: "\{\{ group.status_badge_tooltip \}\}", - awToolTip: "\{\{ group.status_badge_tooltip \}\}", - dataPlacement: 'top', - badgeTipPlacement: 'top', - badgePlacement: 'left', - searchOptions: [ - { name: "failed", value: "failed" }, - { name: "never", value: "never updated" }, - { name: "n/a", value: "none" }, - { name: "successful", value: "successful" }, - { name: "updating", value: "updating" }], - sourceModel: 'inventory_source', - sourceField: 'status' - }, - failed_hosts: { - label: 'Failed Hosts', - ngHref: "\{\{ group.failed_hosts_link \}\}", - badgeIcon: "\{\{ 'fa icon-failures-' + group.failed_hosts_class \}\}", - badgeNgHref: "\{\{ group.failed_hosts_link \}\}", - badgePlacement: 'left', - badgeToolTip: "\{\{ group.failed_hosts_tip \}\}", - badgeTipPlacement: 'top', - awToolTip: "\{\{ group.failed_hosts_tip \}\}", - dataPlacement: "top", - searchable: false, - excludeModal: true, - sortField: "hosts_with_active_failures" + hasChildren: true, + columnClass: 'col-lg-9' }, source: { label: 'Source', @@ -99,6 +68,9 @@ angular.module('InventoryGroupsDefinition', []) }, actions: { + + columnClass: 'col-lg-3', + create: { mode: 'all', ngClick: "createGroup()", @@ -135,29 +107,46 @@ angular.module('InventoryGroupsDefinition', []) }, fieldActions: { + sync_status: { + mode: 'all', + ngClick: "viewUpdateStatus(\{\{ group.id \}\})", + awToolTip: "\{\{ group.status_tooltip \}\}", + ngClass: "group.status_class", + dataPlacement: "top" + }, + failed_hosts: { + mode: 'all', + awToolTip: "\{\{ group.hosts_status_tip \}\}", + dataPlacement: "top", + ngClick: "viewFailedHosts(\{\{ group.id \}\})", + iconClass: "\{\{ 'fa icon-failures-' + group.hosts_status_class \}\}" + }, group_update: { - label: 'Sync', - ngClick: 'updateGroup(\{\{ group.group_id \}\})', - awToolTip: "\{\{ group.update_tooltip \}\}", - ngClass: "group.update_class", - awToolTip: "Start inventory sync" + //label: 'Sync', + ngClick: 'updateGroup(\{\{ group.id \}\})', + awToolTip: "\{\{ group.launch_tooltip \}\}", + ngClass: "group.launch_class", + dataPlacement: "top" }, cancel: { - label: 'Cancel', - ngClick: "cancelUpdate(\{\{ group.group_id \}\}, '\{\{ group.name \}\}')", - awToolTip: "\{\{ group.cancel_tooltip \}\}", + //label: 'Cancel', + ngClick: "cancelUpdate(\{\{ group.id \}\}, '\{\{ group.name \}\}')", + awToolTip: "Cancel sync process", ngClass: "group.cancel_class", - ngShow: "group.status == 'running' || group.status == 'pending'" + ngShow: "group.status == 'running' || group.status == 'pending'", + dataPlacement: "top" }, edit: { - label: 'Edit', + //label: 'Edit', ngClick: "editGroup(\{\{ group.group_id \}\})", - awToolTip: 'Edit group' + awToolTip: 'Edit group', + dataPlacement: "top" }, "delete": { - label: 'Delete', + //label: 'Delete', ngClick: "deleteGroup(\{\{ group.group_id \}\},'\{\{ group.name \}\}')", - awToolTip: 'Delete group' + awToolTip: 'Delete group', + dataPlacement: "top" } } }); diff --git a/awx/ui/static/js/lists/InventoryHosts.js b/awx/ui/static/js/lists/InventoryHosts.js index 750197b14d..28307a146e 100644 --- a/awx/ui/static/js/lists/InventoryHosts.js +++ b/awx/ui/static/js/lists/InventoryHosts.js @@ -102,9 +102,9 @@ angular.module('InventoryHostsDefinition', []) actions: { create: { mode: 'all', - ngClick: "createGroup()", - ngHide: "groupCreateHide", - ngDisabled: 'grpBtnDisabled', + ngClick: "createHost()", + ngHide: "hostCreateHide", + ngDisabled: 'BtnDisabled', awToolTip: "Create a new host" }, stream: { diff --git a/awx/ui/static/js/lists/JobEvents.js b/awx/ui/static/js/lists/JobEvents.js index 8bb7905688..da280dec66 100644 --- a/awx/ui/static/js/lists/JobEvents.js +++ b/awx/ui/static/js/lists/JobEvents.js @@ -70,7 +70,8 @@ angular.module('JobEventsListDefinition', []) nosort: true, searchable: false, ngClass: '\{\{ jobevent.class \}\}', - appendHTML: 'jobevent.event_detail' + appendHTML: 'jobevent.event_detail', + 'columnClass': 'col-lg-4' }, host: { label: 'Host', @@ -80,7 +81,7 @@ angular.module('JobEventsListDefinition', []) nosort: true, searchOnly: false, id: 'job-event-host-header', - columnClass: 'hidden-sm' + columnClass: 'col-lg-3 hidden-sm hidden-xs' } }, diff --git a/awx/ui/static/less/animations.less b/awx/ui/static/less/animations.less new file mode 100644 index 0000000000..3f0046918f --- /dev/null +++ b/awx/ui/static/less/animations.less @@ -0,0 +1,27 @@ +/********************************************* + * Copyright (c) 2014 AnsibleWorks, Inc. + * + * animations.css + * + * custom animation mixins for ansible-ui + * + */ + + +.pulsate(@duration: 1.5s) { + -webkit-animation:pulsate @duration linear infinite alternate; + -moz-animation:pulsate @duration linear infinite alternate; + animation:pulsate @duration linear infinite alternate; +} +@-webkit-keyframes pulsate { + 0% { -moz-transform: scale(.3); opacity: .2; } + 100% { -moz-transform: scale(1.1); opacity: 1; } +} +@-webkit-keyframes pulsate { + 0% { -webkit-transform: scale(.3); opacity: .2; } + 100% { -webkit-transform: scale(1.1); opacity: 1; } +} +@keyframes pulsate { + 0% { -webkit-transform: scale(.3); transform:scale(.3); opacity: .2;} + 100% { -webkit-transform: scale(1.1); transform:scale(1.1); opacity: 1;} +} \ No newline at end of file diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 711c06e16b..7d8204c523 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -7,6 +7,9 @@ * */ +@import "animations.less"; + + @black: #171717; @white: #FFF; @warning: #FF9900; @@ -79,6 +82,10 @@ a:focus { box-shadow: none; } +.btn-disabled { + cursor: not-allowed; +} + /* Bring primary (blue) buttons in line with link colors */ .btn-primary { background-color: @blue; @@ -801,6 +808,13 @@ input[type="checkbox"].checkbox-no-label { content: "\f111"; } + .icon-failures-none, + .icon-failures-na, + .icon-failures-true, + .icon-failures-false { + font-size: 12px; + } + .badge { padding: 2px 3px 3px 4px; font-size: 10px; @@ -821,16 +835,18 @@ input[type="checkbox"].checkbox-no-label { /* Cloud inventory status. i.e. inventory_source.status values */ + /* .icon-cloud-na:before, .icon-cloud-never:before, .icon-cloud-updating:before, .icon-cloud-successful:before { content: "\f111"; } - + .icon-cloud-failed:before { content: "\f06a"; } + */ .icon-cloud-na, .icon-cloud-never { @@ -846,6 +862,10 @@ input[type="checkbox"].checkbox-no-label { color: @red; } + .icon-cloud-updating { + .pulsate(); + } + .icon-enabled-true:before { content: "\f046"; } @@ -983,8 +1003,20 @@ input[type="checkbox"].checkbox-no-label { font-weight: bold; } +.inv-group-toggle { + margin-right: 3px; +} + .disabled { + color: @grey; +} + +a.disabled:hover { color: @grey; + cursor: not-allowed; +} +a.btn-disabled:hover { + cursor: not-allowed; } .parse-selection { @@ -1291,6 +1323,7 @@ tr td button i { } + /* Landscape phone to portrait tablet */ @media (max-width: 767px) { diff --git a/awx/ui/static/lib/ansible/InventoryTree.js b/awx/ui/static/lib/ansible/InventoryTree.js index b7c07355df..55b79d3af8 100644 --- a/awx/ui/static/lib/ansible/InventoryTree.js +++ b/awx/ui/static/lib/ansible/InventoryTree.js @@ -171,8 +171,8 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper']) } }]) - .factory('BuildTree', ['Rest', 'GetBasePath', 'ProcessErrors', 'SortNodes', 'Wait', 'UpdateStatusMsg', - function(Rest, GetBasePath, ProcessErrors, SortNodes, Wait, UpdateStatusMsg) { + .factory('BuildTree', ['Rest', 'GetBasePath', 'ProcessErrors', 'SortNodes', 'Wait', 'GetSyncStatusMsg', 'GetHostsStatusMsg', + function(Rest, GetBasePath, ProcessErrors, SortNodes, Wait, GetSyncStatusMsg, GetHostsStatusMsg) { return function(params) { var inventory_id = params.inventory_id; @@ -185,32 +185,50 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper']) function buildGroups(tree_data, parent, level) { var sorted = SortNodes(tree_data); for (var i=0; i < sorted.length; i++) { - var currentId = id; - var stat = UpdateStatusMsg({ status: sorted[i].summary_fields.inventory_source.status }); - var group = { - name: sorted[i].name, - has_active_failures: sorted[i].has_active_failures, - total_hosts: sorted[i].total_hosts, - hosts_with_active_failures: sorted[i].hosts_with_active_failures, - total_groups: sorted[i].total_groups, - groups_with_active_failures: sorted[i].groups_with_active_failures, - parent: parent, - has_children: (sorted[i].children.length > 0) ? true : false, - has_inventory_sources: sorted[i].has_inventory_sources, - id: id, - group_id: sorted[i].id, - event_level: level, - ngicon: (sorted[i].children.length > 0) ? 'icon-collapse-alt' : null, - related: { children: (sorted[i].children.length > 0) ? sorted[i].related.children : '' }, - status: sorted[i].summary_fields.inventory_source.status, - status_badge_class: stat['class'], - status_badge_tooltip: stat['tooltip'], - selected_class: '' - } - groups.push(group); id++; + var stat = GetSyncStatusMsg({ + status: sorted[i].summary_fields.inventory_source.status + }); // from helpers/Groups.js + var hosts_status = GetHostsStatusMsg({ + active_failures: sorted[i].hosts_with_active_failures, + total_hosts: sorted[i].total_hosts, + inventory_id: inventory_id, + group_id: sorted[i].id + }); // from helpers/Groups.js + var group = { + name: sorted[i].name, + has_active_failures: sorted[i].has_active_failures, + total_hosts: sorted[i].total_hosts, + hosts_with_active_failures: sorted[i].hosts_with_active_failures, + total_groups: sorted[i].total_groups, + groups_with_active_failures: sorted[i].groups_with_active_failures, + parent: parent, + has_children: (sorted[i].children.length > 0) ? true : false, + has_inventory_sources: sorted[i].has_inventory_sources, + id: id, + source: sorted[i].summary_fields.inventory_source.source, + group_id: sorted[i].id, + event_level: level, + ngicon: (sorted[i].children.length > 0) ? 'fa fa-minus-square-o inv-group-toggle' : null, + ngclick: 'toggle(' + id + ')', + related: { + children: (sorted[i].children.length > 0) ? sorted[i].related.children : '', + inventory_source: sorted[i].related.inventory_source + }, + status: sorted[i].summary_fields.inventory_source.status, + status_class: stat['class'], + status_tooltip: stat['tooltip'], + launch_tooltip: stat['launch_tip'], + launch_class: stat['launch_class'], + hosts_status_tip: hosts_status['tooltip'], + hosts_status_link: hosts_status['link'], + hosts_status_class: hosts_status['class'], + selected_class: '', + show: true + } + groups.push(group); if (sorted[i].children.length > 0) { - buildGroups(sorted[i].children, currentId, level + 1); + buildGroups(sorted[i].children, id, level + 1); } } } @@ -224,6 +242,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper']) Rest.get() .success( function(data, status, headers, config) { buildGroups(data, 0, 0); + //console.log(groups); scope.$emit('searchTreeReady', inventory_name, groups); }) .error( function(data, status, headers, config) { diff --git a/awx/ui/static/lib/ansible/generator-helpers.js b/awx/ui/static/lib/ansible/generator-helpers.js index 0a71575317..da6d458782 100644 --- a/awx/ui/static/lib/ansible/generator-helpers.js +++ b/awx/ui/static/lib/ansible/generator-helpers.js @@ -119,8 +119,9 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers']) case 'delete': icon = "fa-trash-o"; break; - case 'update': case 'group_update': + icon = 'fa-exchange'; + break; case 'scm_update': icon = 'fa-cloud-download'; break; @@ -152,7 +153,10 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers']) break; case 'view': icon="fa-search-plus"; - break; + break; + case 'sync_status': + icon="fa-cloud"; + break; } icon += (size) ? " " + size : ""; return Icon(icon); diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index 3cb4f7716a..0e34aff63c 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -285,7 +285,9 @@ angular.module('ListGenerator', ['GeneratorHelpers']) html += "Select"; } else if (options.mode == 'edit') { - html += "Actions\n"; + html += "Actions\n"; } html += "\n"; html += "\n"; @@ -319,7 +321,9 @@ angular.module('ListGenerator', ['GeneratorHelpers']) "ng-false-value=\"0\" id=\"check_{{" + list.iterator + ".id}}\" />"; } else if (options.mode == 'edit' || options.mode == 'summary') { + // Row level actions + html += ""; for (action in list.fieldActions) { if (list.fieldActions[action].type && list.fieldActions[action].type == 'DropDown') { @@ -336,11 +340,18 @@ angular.module('ListGenerator', ['GeneratorHelpers']) var fAction = list.fieldActions[action]; html += ""; + } + else { + html += SelectIcon({ action: action }); + } html += (fAction.label) ? " " + list.fieldActions[action]['label'] : ""; html += ""; }