diff --git a/awx/ui/static/js/forms/Inventories.js b/awx/ui/static/js/forms/Inventories.js index 7705ad4282..0a52a2e146 100644 --- a/awx/ui/static/js/forms/Inventories.js +++ b/awx/ui/static/js/forms/Inventories.js @@ -17,12 +17,18 @@ angular.module('InventoryFormDefinition', []) well: true, navigationLinks: { + inventory: { + href: "/#/inventories/{{ inventory_id }}", + label: "Inventory Properties", + icon: "icon-edit", + active: true + }, hosts: { href: "/#/inventories/{{ inventory_id }}/hosts", label: 'Hosts', icon: 'icon-laptop' }, - Groups: { + groups: { href: "/#/inventories/{{ inventory_id }}/groups", label: 'Groups', icon: 'icon-sitemap' diff --git a/awx/ui/static/js/forms/InventoryHosts.js b/awx/ui/static/js/forms/InventoryHosts.js index 3e4051ba2e..3fd5d9c20f 100644 --- a/awx/ui/static/js/forms/InventoryHosts.js +++ b/awx/ui/static/js/forms/InventoryHosts.js @@ -32,25 +32,6 @@ angular.module('InventoryHostsFormDefinition', []) sourceModel: 'groups', sourceField: 'name', nosort: true - }, - dropdown: { - type: 'DropDown', - searchable: false, - nosort: true, - label: 'Jobs', - "class": "btn-sm", - //ngDisabled: 'host.last_job == null', - options: [ - { ngClick: "allJobs(\{\{ host.id \}\})", label: 'All jobs', ngShow: 'host.last_job' }, - { ngClick: "allHostSummaries(\{\{ host.id \}\},'\{\{ host.name \}\}', \{\{ inventory_id \}\})", label: 'All host summaries', - ngShow: 'host.last_job' }, - { ngClick: 'viewJobs(\{\{ host.last_job \}\})', label: 'Latest job', ngShow: 'host.last_job' }, - { ngClick: "viewLastEvents(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " + - "'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest job events', ngShow: 'host.last_job' }, - { ngClick: "viewLastSummary(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " + - "'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest host summary', ngShow: 'host.last_job' }, - { ngClick: "", label: 'No job data available', ngShow: 'host.last_job == null' } - ] } }, @@ -76,6 +57,25 @@ angular.module('InventoryHostsFormDefinition', []) }, fieldActions: { + + ViewJobs: { + type: 'DropDown', + label: 'Jobs', + icon: 'icon-zoom-in', + "class": "btn-default btn-sm", + options: [ + { ngClick: "allJobs(\{\{ host.id \}\})", label: 'All jobs', ngShow: 'host.last_job' }, + { ngClick: "allHostSummaries(\{\{ host.id \}\},'\{\{ host.name \}\}', \{\{ inventory_id \}\})", label: 'All host summaries', + ngShow: 'host.last_job' }, + { ngClick: 'viewJobs(\{\{ host.last_job \}\})', label: 'Latest job', ngShow: 'host.last_job' }, + { ngClick: "viewLastEvents(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " + + "'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest job events', ngShow: 'host.last_job' }, + { ngClick: "viewLastSummary(\{\{ host.id \}\}, '\{\{ host.last_job \}\}', '\{\{ host.name \}\}', " + + "'\{\{ host.summary_fields.last_job.name \}\}')", label: 'Latest host summary', ngShow: 'host.last_job' }, + { ngClick: "", label: 'No job data available', ngShow: 'host.last_job == null' } + ] + }, + "delete": { ngClick: "deleteHost(\{\{ host.id \}\},'\{\{ host.name \}\}')", icon: 'icon-trash', diff --git a/awx/ui/static/js/lists/Inventories.js b/awx/ui/static/js/lists/Inventories.js index fa2308b632..d72856af68 100644 --- a/awx/ui/static/js/lists/Inventories.js +++ b/awx/ui/static/js/lists/Inventories.js @@ -28,7 +28,8 @@ angular.module('InventoriesListDefinition', []) badgeTipPlacement: 'bottom' }, description: { - label: 'Description' + label: 'Description', + link: true }, organization: { label: 'Organization', @@ -56,37 +57,25 @@ angular.module('InventoriesListDefinition', []) type: 'DropDown', label: 'Jobs', icon: 'icon-zoom-in', - 'class': 'btn-xs', + 'class': 'btn-default btn-xs', options: [ { ngClick: 'viewJobs(\{\{ inventory.id \}\})', label: 'All Jobs' }, { ngClick: "viewFailedJobs(\{\{ inventory.id \}\})", label: 'Failed jobs' } ] }, - - hosts: { - label: 'Hosts', - ngClick: "editHosts(\{\{ inventory.id \}\})", - icon: 'icon-laptop', - "class": 'btn-xs btn-default', - awToolTip: 'Edit Hosts' - }, - - groups: { - label: 'Groups', - ngClick: "editGroups(\{\{ inventory.id \}\})", - icon: 'icon-sitemap', - "class": 'btn-xs btn-default', - awToolTip: 'Edit Groups' - }, - - edit: { - ngClick: "editInventory(\{\{ inventory.id \}\})", + edit: { + type: 'DropDown', + label: 'Edit', icon: 'icon-edit', - "class": 'btn-xs btn-default', - awToolTip: 'Edit Inventory Properties' + 'class': 'btn-default btn-xs', + options: [ + { ngClick: "editInventory(\{\{ inventory.id \}\})", label: 'Inventory Properties' }, + { ngClick: "editHosts(\{\{ inventory.id \}\})", label: 'Hosts' }, + { ngClick: "editGroups(\{\{ inventory.id \}\})", label: 'Groups' } + ] }, - "delete": { + label: 'Delete', ngClick: "deleteInventory(\{\{ inventory.id \}\},'\{\{ inventory.name \}\}')", icon: 'icon-trash', "class": 'btn-xs btn-danger', diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 025c733523..e3fd924031 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -423,6 +423,8 @@ legend { vertical-align: middle; } + +/* breadcrumbs */ .nav-path { padding: 5px 0 10px 0; margin-right: 5px; @@ -433,15 +435,57 @@ legend { border: 1px solid #d8d8d8; border-radius: 6px; box-shadow: 3px 3px 4px 0 #808080; + + .breadcrumb { + display: inline-block; + padding-bottom: 0; + padding-left: 0; + padding-right: 0; + margin-bottom: 0; + margin-left: 10px; + } + + .dropdown { + display: inline-block; + margin-right: 0; + paddding-right: 0; + + .toggle, .toggle:visited, .toggle:hover, .toggle:active { + color: @black; + } + + li a.active { + color: @grey; + } + + } + } -.nav-path ul { - padding-bottom: 0; - padding-left: 0; - margin-bottom: 0; - margin-left: 10px; +.actions .dropdown { + display: inline-block; } +/* Display drop-down menus on hover. Remove margin between toggle link + and menu, allowing smooth mouse movement between toggle and menu + + http://stackoverflow.com/questions/8878033/how-to-make-twitter-bootstrap-menu-dropdown-on-hover-rather-than-click + */ + .dropdown-toggle:hover .dropdown-menu, .dropdown:hover .dropdown-menu { + display: block; + } + + .dropdown-menu li:hover { + visibility: visible; + } + + .dropdown-menu { + margin-top: 0; + } + +/* end */ + + .greeting { padding-right: 22px; } @@ -996,7 +1040,7 @@ tr td button i { white-space: normal; } - td.actions button { + td.actions .btn { width: 75px; margin-bottom: 5px; } diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index fe7bd2ab83..e71196cd95 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -10,8 +10,9 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) .factory('GenerateForm', [ '$location', '$cookieStore', '$compile', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'Column', - 'NavigationLink', 'HelpCollapse', 'Button', - function($location, $cookieStore, $compile, SearchWidget, PaginateWidget, Attr, Icon, Column, NavigationLink, HelpCollapse, Button) { + 'NavigationLink', 'HelpCollapse', 'Button', 'DropDown', + function($location, $cookieStore, $compile, SearchWidget, PaginateWidget, Attr, Icon, Column, NavigationLink, HelpCollapse, Button, + DropDown) { return { setForm: function(form) { @@ -844,19 +845,48 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) return html; }, - breadCrumbs: function(options) { + breadCrumbs: function(options, navigation) { var html = ''; + html += "
\n"; html += "\n"; + html += "
\n"; + for (var itm in navigation) { + if (navigation[itm].active) { + html += "" + + navigation[itm].label + " "; + break; + } + } + html += "\n"; + html += "
\n"; + html += "
\n"; } else { - html += this.form.addTitle; + html += "
  • "; + if (options.mode == 'edit') { + html += this.form.editTitle; + } + else { + html += this.form.addTitle; + } + html += "
  • \n\n\n"; } - html += "\n\n\n"; return html; }, @@ -868,17 +898,14 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) var html = ''; if (!this.modal) { - html += this.breadCrumbs(options); + if (this.form.navigationLinks) { + html += this.breadCrumbs(options, this.form.navigationLinks); + } + else { + html += this.breadCrumbs(options); + } } - if (!this.modal && this.form.navigationLinks) { - html += "\n"; - } - if ((!this.modal && this.form.statusFields)) { // Add status fields section (used in Jobs form) html += "
    \n"; @@ -920,17 +947,6 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) html += "

    " + this.form.collapseTitle + "

    \n"; html += "
    \n"; } - - /* if (this.form.navigation) { - html += "
    \n"; - for (btn in this.form.navigation) { - var btn = this.form.navigation[btn]; - if ( btn.position.indexOf('top-left') >= 0 || btn.position.indexOf('top-right') >= 0 ) { - html += this.button(btn, 'top'); - } - } - html += "
    \n"; - } */ // Start the well if ( this.has('well') ) { @@ -1059,17 +1075,6 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) html += "
    \n"; } - /*if (this.form.navigation) { - html += "
    \n"; - for (btn in this.form.navigation) { - var btn = this.form.navigation[btn]; - if ( btn.position.indexOf('bottom-left') >= 0 || btn.position.indexOf('bottom-right') >= 0 ) { - html += this.button(btn, 'bottom'); - } - } - html += "
    \n"; - }*/ - if ( this.form.collapse && this.form.collapseMode == options.mode ) { html += "
    \n"; html += "\n"; @@ -1117,29 +1122,37 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) // // Used to create the inventory detail view // - - function navigationLinks(page) { - // Returns html for navigation links - var html = "\n"; - return html; - } var form = this.form; var itm = "groups"; - var html = ''; + var navigation = { + inventory: { + href: "/#/inventories/{{ inventory_id }}", + label: "Inventory Properties", + icon: "icon-edit" + }, + hosts: { + href: "/#/inventories/{{ inventory_id }}/hosts", + label: 'Hosts', + icon: 'icon-laptop' + }, + groups: { + href: "/#/inventories/{{ inventory_id }}/groups", + label: 'Groups', + icon: 'icon-sitemap' + } + }; - html += this.breadCrumbs(options); - + if (form.type == 'groupsview') { + + navigation.inventory.active = false; + navigation.hosts.active = false; + navigation.groups.active = true; + + html += this.breadCrumbs(options, navigation); + // build the groups page html += "
    \n"; html += "\n"; @@ -1147,15 +1160,12 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) "use the Inventories->Hosts page to " + "add hosts to the group.

    "; html += "
    \n"; - - html += navigationLinks('group'); - html += "
    \n"; html += "
    \n"; html += "
    \n"; html += "\n"; + "properties\n"; html += "\n"; html += "\n"; @@ -1191,8 +1207,6 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) html += "
    \n"; html += "
    \n"; - html += navigationLinks('host'); - html += "
    \n"; html += SearchWidget({ iterator: form.iterator, template: form, mini: true, size: 'col-md-6 col-lg-6'}); @@ -1292,16 +1306,21 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies']) html += ""; for (act in form.fieldActions) { var action = form.fieldActions[act]; - html += "\n"; + */ + html += "
    \n"; + html += "\n"; html += "
      \n"; for (var i=0; i < field.options.length; i++) { html += "