diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index 0e6a08978b..b3dd266a63 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -60,6 +60,7 @@ angular.module('Tower', [ 'CredentialFormDefinition', 'LookUpHelper', 'JobTemplatesListDefinition', + 'PortalJobTemplatesListDefinition', 'JobTemplateFormDefinition', 'JobTemplatesHelper', 'JobSubmissionHelper', @@ -86,7 +87,7 @@ angular.module('Tower', [ 'HostPieChartWidget', 'HostGraphWidget', 'DashboardJobsWidget', - 'PortalJobTemplateWidget', + 'PortalJobsWidget', 'StreamWidget', 'JobsHelper', 'InventoryGroupsHelpDefinition', @@ -117,7 +118,8 @@ angular.module('Tower', [ 'LoadConfigHelper', 'SocketHelper', 'AboutAnsibleHelpModal', - 'SurveyQuestionFormDefinition' + 'SurveyQuestionFormDefinition', + 'PortalJobsListDefinition' ]) .constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/') diff --git a/awx/ui/static/js/controllers/Portal.js b/awx/ui/static/js/controllers/Portal.js index 6698de9822..5931715da4 100644 --- a/awx/ui/static/js/controllers/Portal.js +++ b/awx/ui/static/js/controllers/Portal.js @@ -25,15 +25,15 @@ * */ function PortalController($scope, $compile, $routeParams, $rootScope, $location, $log, Wait, ClearScope, Stream, Rest, GetBasePath, ProcessErrors, - Button, PortalJobTemplate, GenerateList, JobTemplateList, SearchInit, PaginateInit, PlaybookRun){ + Button, PortalJobsWidget, GenerateList, PortalJobTemplateList, SearchInit, PaginateInit, PlaybookRun){ ClearScope('portal'); var html, e, - winHeight, - available_height, - list = JobTemplateList, + // winHeight, + // available_height, + list = PortalJobTemplateList, view= GenerateList, defaultUrl = GetBasePath('job_templates'), buttons = { @@ -71,9 +71,9 @@ function PortalController($scope, $compile, $routeParams, $rootScope, $location, $scope.removeLoadPortal(); } $scope.removeLoadPortal = $scope.$on('LoadPortal', function () { - winHeight = $(window).height(); - available_height = Math.floor(winHeight - $('#main-menu-container .navbar').outerHeight() - $('#refresh-row').outerHeight() - 30); - $('.portal-container').height(available_height); + // winHeight = $(window).height(); + // available_height = Math.floor(winHeight - $('#main-menu-container .navbar').outerHeight() - $('#refresh-row').outerHeight() - 30); + // $('.portal-container').height(available_height); view.inject( list, { id : 'portal-job-template', @@ -106,7 +106,7 @@ function PortalController($scope, $compile, $routeParams, $rootScope, $location, $scope.search(list.iterator); - PortalJobTemplate({ + PortalJobsWidget({ scope: $scope, target: 'portal-jobs', // dashboard: data @@ -136,5 +136,5 @@ function PortalController($scope, $compile, $routeParams, $rootScope, $location, } PortalController.$inject = ['$scope', '$compile', '$routeParams', '$rootScope', '$location', '$log','Wait', 'ClearScope', 'Stream', 'Rest', 'GetBasePath', 'ProcessErrors', - 'Button', 'PortalJobTemplate', 'GenerateList' , 'JobTemplateList', 'SearchInit', 'PaginateInit', 'PlaybookRun' + 'Button', 'PortalJobsWidget', 'GenerateList' , 'PortalJobTemplateList', 'SearchInit', 'PaginateInit', 'PlaybookRun' ]; diff --git a/awx/ui/static/js/lists/PortalJobTemplates.js b/awx/ui/static/js/lists/PortalJobTemplates.js new file mode 100644 index 0000000000..45a3395872 --- /dev/null +++ b/awx/ui/static/js/lists/PortalJobTemplates.js @@ -0,0 +1,47 @@ +/********************************************* + * Copyright (c) 2014 AnsibleWorks, Inc. + * + * PoralJobTemplates.js + * List view object for Job Templates data model. + * + * + */ + +'use strict'; + +angular.module('PortalJobTemplatesListDefinition', []) + .value('PortalJobTemplateList', { + + name: 'job_templates', + iterator: 'job_template', + selectTitle: 'Add Job Template', + editTitle: 'Job Templates', + selectInstructions: "Click on a row to select it, and click Finished when done. Use the " + + "button to create a new job template.", + index: false, + hover: true, + + fields: { + name: { + label: 'Name', + columnClass: 'col-lg-5 col-md-5 col-sm-9 col-xs-8' + }, + description: { + label: 'Description', + columnClass: 'col-lg-4 col-md-4 hidden-sm hidden-xs' + } + }, + + actions: { + }, + + fieldActions: { + submit: { + label: 'Launch', + mode: 'all', + ngClick: 'submitJob(job_template.id)', + awToolTip: 'Start a job using this template', + dataPlacement: 'top' + } + } + }); diff --git a/awx/ui/static/js/lists/PortalJobs.js b/awx/ui/static/js/lists/PortalJobs.js new file mode 100644 index 0000000000..5f05415861 --- /dev/null +++ b/awx/ui/static/js/lists/PortalJobs.js @@ -0,0 +1,81 @@ +/********************************************* + * Copyright (c) 2014 AnsibleWorks, Inc. + * + * PortalJobs.js + * List view object for portal job data model. + * + * Used on dashboard to provide a list of all jobs, regardless of + * status. + * + */ + +'use strict'; + +angular.module('PortalJobsListDefinition', []) + .value( 'PortalJobsList', { + + name: 'jobs', + iterator: 'job', + editTitle: 'Jobs', + // 'class': 'table-condensed', + index: false, + hover: true, + well: false, + + fields: { + id: { + label: 'ID', + //ngClick:"viewJobLog(job.id)", + key: true, + noLink: true, //undocumented: 'key' above will automatically made the fields a link, but 'noLink' will override this setting + desc: true, + searchType: 'int', + columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2', + // awToolTip: "{{ job.status_tip }}", + // awTipPlacement: "top", + }, + status: { + label: 'Status', + columnClass: 'col-lg-1 col-md-2 col-sm-2 col-xs-2', + // awToolTip: "{{ job.status_tip }}", + // awTipPlacement: "top", + dataTitle: "{{ job.status_popover_title }}", + icon: 'icon-job-{{ job.status }}', + iconOnly: true, + // ngClick:"viewJobLog(job.id)", + searchable: true, + nosort: true, + searchType: 'select', + searchOptions: [ + { name: "Success", value: "successful" }, + { name: "Error", value: "error" }, + { name: "Failed", value: "failed" }, + { name: "Canceled", value: "canceled" } + ] + }, + started: { + label: 'Started', + noLink: true, + searchable: false, + filter: "date:'MM/dd HH:mm:ss'", + columnClass: "col-lg-3 col-md-3 hidden-xs" + }, + name: { + label: 'Name', + columnClass: 'col-md-5 col-xs-5', + //ngClick: "viewJobLog(job.id, job.nameHref)", + defaultSearchField: true + } + }, + + actions: { }, + + fieldActions: { + job_details: { + mode: 'all', + ngClick: "viewJobLog(job.id)", + awToolTip: 'View job details', + dataPlacement: 'top' + } + } + }); diff --git a/awx/ui/static/js/widgets/PortalJobs.js b/awx/ui/static/js/widgets/PortalJobs.js new file mode 100644 index 0000000000..b337dc930d --- /dev/null +++ b/awx/ui/static/js/widgets/PortalJobs.js @@ -0,0 +1,178 @@ +/********************************************* + * Copyright (c) 2014 AnsibleWorks, Inc. + */ + /** + * @ngdoc function + * @name widgets.function:DashboardJobs + * @description + * + */ + +'use strict'; + +angular.module('PortalJobsWidget', ['RestServices', 'Utilities']) +.factory('PortalJobsWidget', ['$rootScope', '$compile', 'LoadSchedulesScope', 'LoadJobsScope', 'PortalJobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath', + function ($rootScope, $compile, LoadSchedulesScope, LoadJobsScope, PortalJobsList, ScheduledJobsList, GetChoices, GetBasePath) { + return function (params) { + var scope = params.scope, + target = params.target, + choicesCount = 0, + listCount = 0, + jobs_scope = scope.$new(true), + scheduled_scope = scope.$new(true), + max_rows, + html, e; + + html = ''; + html += "
\n"; + // html+= "Job Templates "; + // html += "\n"; + // html += "
\n"; + html += "
\n"; + html += "
\n"; + html += "
\n"; + html += "
\n"; //row + html += "
\n"; + html += "
\n"; + html += "
\n"; //list + html += "
\n"; //active-jobs-tab + // html += "
\n"; + // html += "
\n"; // jobs-list-container + html += "
\n"; + + e = angular.element(document.getElementById(target)); + e.html(html); + $compile(e)(scope); + + if (scope.removeListLoaded) { + scope.removeListLoaded(); + } + scope.removeListLoaded = scope.$on('listLoaded', function() { + listCount++; + if (listCount === 1) { + //api_complete = true; + scope.$emit('WidgetLoaded', "dashboard_jobs", jobs_scope, scheduled_scope); + } + }); + + // After all choices are ready, load up the lists and populate the page + if (scope.removeBuildJobsList) { + scope.removeBuildJobsList(); + } + scope.removeBuildJobsList = scope.$on('buildJobsList', function() { + if (PortalJobsList.fields.type) { + PortalJobsList.fields.type.searchOptions = scope.type_choices; + } + LoadJobsScope({ + parent_scope: scope, + scope: jobs_scope, + list: PortalJobsList, + id: 'active-jobs', + url: GetBasePath('unified_jobs') + '?status__in=running,completed,failed,successful,error,canceled', + pageSize: max_rows, + spinner: false + }); + // LoadSchedulesScope({ + // parent_scope: scope, + // scope: scheduled_scope, + // list: ScheduledJobsList, + // id: 'scheduled-jobs-tab', + // url: GetBasePath('schedules') + '?next_run__isnull=false', + // pageSize: max_rows, + // spinner: false + // }); + + $(window).resize(_.debounce(function() { + resizePortalJobsWidget(); + }, 500)); + }); + + if (scope.removeChoicesReady) { + scope.removeChoicesReady(); + } + scope.removeChoicesReady = scope.$on('choicesReady', function() { + choicesCount++; + if (choicesCount === 2) { + setPortalJobsHeight(); + scope.$emit('buildJobsList'); + } + }); + + GetChoices({ + scope: scope, + url: GetBasePath('unified_jobs'), + field: 'status', + variable: 'status_choices', + callback: 'choicesReady' + }); + + GetChoices({ + scope: scope, + url: GetBasePath('unified_jobs'), + field: 'type', + variable: 'type_choices', + callback: 'choicesReady' + }); + + + + // Set the height of each container and calc max number of rows containers can hold + function setPortalJobsHeight() { + var docw = $(window).width(), + box_height, available_height, search_row, page_row, height, header, row_height; + + available_height = Math.floor($(window).height() - $('#main-menu-container .navbar').outerHeight() - $('#refresh-row').outerHeight() - 30); + $('.portal-job-template-container').height(available_height); + $('.portal-container').height(available_height); + search_row = Math.max($('.search-row:eq(0)').outerHeight(), 50); + page_row = Math.max($('.page-row:eq(0)').outerHeight(), 33); + header = 0; //Math.max($('#completed_jobs_table thead').height(), 41); + height = Math.floor(available_height) - header - page_row - search_row ; + if (docw < 765 && docw >= 493) { + row_height = 27; + } + else if (docw < 493) { + row_height = 47; + } + else if (docw < 865) { + row_height = 87; + } + else if (docw < 925) { + row_height = 67; + } + else if (docw < 1415) { + row_height = 47; + } + else { + row_height = 35; + } + max_rows = Math.floor(height / row_height); + if (max_rows < 5){ + box_height = header+page_row + search_row + 40 + (5 * row_height); + if (docw < 1140) { + box_height += 40; + } + $('.portal-job-template-container').height(box_height); + max_rows = 5; + } + } + + // Set container height and return the number of allowed rows + function resizePortalJobsWidget() { + setPortalJobsHeight(); + jobs_scope[PortalJobsList.iterator + '_page_size'] = max_rows; + jobs_scope.changePageSize(PortalJobsList.name, PortalJobsList.iterator, false); + // scheduled_scope[ScheduledJobsList.iterator + '_page_size'] = max_rows; + // scheduled_scope.changePageSize(ScheduledJobsList.name, ScheduledJobsList.iterator, false); + } + + + + }; +} +]); diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 4a4db087aa..d27e3e4b2c 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -114,8 +114,10 @@ + + @@ -167,7 +169,7 @@ - +