From eabe4bdf023b626797c517a8e1c597ced6bc6a65 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Fri, 10 Apr 2015 14:29:46 -0400 Subject: [PATCH] Made jobs page use 1 pane with tabs instead of the 4 panes divied into completed, running, and queued jobs --- awx/ui/static/js/app.js | 66 +-------- awx/ui/static/js/controllers/Jobs.js | 127 ++++++------------ awx/ui/static/js/filters.js | 6 +- awx/ui/static/js/filters/capitalize.filter.js | 6 + awx/ui/static/js/helpers/Jobs.js | 14 +- awx/ui/static/js/lists.js | 2 + awx/ui/static/js/lists/AllJobs.js | 113 ++++++++++++++++ awx/ui/static/js/shared/generator-helpers.js | 3 + awx/ui/static/partials/jobs.html | 60 ++++----- 9 files changed, 205 insertions(+), 192 deletions(-) create mode 100644 awx/ui/static/js/filters/capitalize.filter.js create mode 100644 awx/ui/static/js/lists/AllJobs.js diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index cc4ed8b2f4..b3af52cbc3 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -22,7 +22,7 @@ import 'tower/forms'; import 'tower/lists'; import 'tower/widgets'; import 'tower/help'; -import 'tower/filters'; +import 'tower/filters'; import {Home, HomeGroups, HomeHosts} from 'tower/controllers/Home'; import {SocketsController} from 'tower/controllers/Sockets'; import {Authenticate} from 'tower/controllers/Authentication'; @@ -123,6 +123,7 @@ var tower = angular.module('Tower', [ 'PermissionListDefinition', 'PermissionsHelper', 'CompletedJobsDefinition', + 'AllJobsDefinition', 'RunningJobsDefinition', 'JobFormDefinition', 'JobSummaryDefinition', @@ -728,69 +729,6 @@ var tower = angular.module('Tower', [ }); }; - // html = ""; - // e = angular.element(document.getElementById('socket-beacon-div')); - // e.empty().append(html); - // $compile(e)($rootScope); - - // e = angular.element(document.getElementById('socket-beacon-li')); - // e.empty().append(html); - // $compile(e)($rootScope); - - // // Listen for job changes and issue callbacks to initiate - // // DOM updates - // function openSocket() { - // sock = Socket({ scope: $rootScope, endpoint: "jobs" }); - // sock.init(); - // sock.on("status_changed", function(data) { - // $log.debug('Job ' + data.unified_job_id + ' status changed to ' + data.status); - // $rootScope.$emit('JobStatusChange', data); - // }); - // sock.on("summary_complete", function(data) { - // $log.debug('Job summary_complete ' + data.unified_job_id); - // $rootScope.$emit('JobSummaryComplete', data); - // }); - // sock.on("schedule_change", function(data) { - // $log.debug('schedule changed to ' + data.status); - // $rootScope.$emit('ScheduleChange', data); - // }); - // } - - // if ($rootScope.removeOpenSocket) { - // $rootScope.removeOpenSocket(); - // } - // $rootScope.removeOpenSocket = $rootScope.$on('OpenSocket', function() { - - // openSocket(); - - // setTimeout(function() { - // $rootScope.$apply(function() { - // sock.checkStatus(); - // $log.debug('socket status: ' + $rootScope.socketStatus); - // }); - // },2000); - - // // monitor socket status - // checkCount = 0; - // setInterval(function() { - // if (sock.checkStatus() === 'error' || checkCount > 5) { - // // there's an error or we're stuck in a 'connecting' state. attempt to reconnect - // $log.debug('socket status: ' + sock.checkStatus()); - // $log.debug('attempting new socket connection'); - // sock = null; - // openSocket(); - // checkCount = 0; - // } - // else if (sock.checkStatus() === 'connecting') { - // checkCount++; - // } - // else { - // checkCount = 0; - // } - // }, 5000); - // }); - }); // end of 'ConfigReady' diff --git a/awx/ui/static/js/controllers/Jobs.js b/awx/ui/static/js/controllers/Jobs.js index 7679037891..6386a5289e 100644 --- a/awx/ui/static/js/controllers/Jobs.js +++ b/awx/ui/static/js/controllers/Jobs.js @@ -14,12 +14,13 @@ */ -export function JobsListController ($rootScope, $log, $scope, $compile, $routeParams, ClearScope, Breadcrumbs, LoadBreadCrumbs, LoadSchedulesScope, - LoadJobsScope, RunningJobsList, CompletedJobsList, QueuedJobsList, ScheduledJobsList, GetChoices, GetBasePath, Wait, Socket) { +export function JobsListController ($rootScope, $log, $scope, $compile, $routeParams, + ClearScope, Breadcrumbs, LoadBreadCrumbs, LoadSchedulesScope, + LoadJobsScope, AllJobsList, ScheduledJobsList, GetChoices, GetBasePath, Wait, Socket) { ClearScope(); - var completed_scope, running_scope, queued_scope, scheduled_scope, + var jobs_scope, scheduled_scope, choicesCount = 0, listCount = 0, api_complete = false, @@ -33,10 +34,10 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $routePa endpoint: "jobs" }); job_socket.init(); - job_socket.on("status_changed", function(data) { - if (api_complete) { - processEvent(data); - } + job_socket.on("status_changed", function() { + // if (api_complete) { + jobs_scope.refreshJobs(); + // } }); schedule_socket = Socket({ scope: $scope, @@ -50,28 +51,6 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $routePa }); } - function processEvent(event) { - switch(event.status) { - case 'running': - running_scope.search('running_job'); - queued_scope.search('queued_job'); - break; - case 'new': - case 'pending': - case 'waiting': - queued_scope.search('queued_job'); - completed_scope.search('completed_job'); - break; - case 'successful': - case 'failed': - case 'error': - case 'canceled': - completed_scope.search('completed_job'); - running_scope.search('running_job'); - queued_scope.search('queued_job'); - } - } - LoadBreadCrumbs(); if ($scope.removeListLoaded) { @@ -79,7 +58,7 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $routePa } $scope.removeListLoaded = $scope.$on('listLoaded', function() { listCount++; - if (listCount === 4) { + if (listCount === 2) { api_complete = true; openSockets(); } @@ -92,71 +71,50 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $routePa $scope.removeBuildJobsList = $scope.$on('buildJobsList', function() { var opt, search_params; - if (CompletedJobsList.fields.type) { - CompletedJobsList.fields.type.searchOptions = $scope.type_choices; - } - if (RunningJobsList.fields.type) { - RunningJobsList.fields.type.searchOptions = $scope.type_choices; - } - if (QueuedJobsList.fields.type) { - QueuedJobsList.fields.type.searchOptions = $scope.type_choices; + if (AllJobsList.fields.type) { + AllJobsList.fields.type.searchOptions = $scope.type_choices; } if ($routeParams.status) { - search_params[CompletedJobsList.iterator + 'SearchField'] = 'status'; - search_params[CompletedJobsList.iterator + 'SelectShow'] = true; - search_params[CompletedJobsList.iterator + 'SearchSelectOpts'] = CompletedJobsList.fields.status.searchOptions; - search_params[CompletedJobsList.iterator + 'SearchFieldLabel'] = CompletedJobsList.fields.status.label.replace(//g,' '); - search_params[CompletedJobsList.iterator + 'SearchType'] = ''; - for (opt in CompletedJobsList.fields.status.searchOptions) { - if (CompletedJobsList.fields.status.searchOptions[opt].value === $routeParams.status) { - search_params[CompletedJobsList.iterator + 'SearchSelectValue'] = CompletedJobsList.fields.status.searchOptions[opt]; + search_params[AllJobsList.iterator + 'SearchField'] = 'status'; + search_params[AllJobsList.iterator + 'SelectShow'] = true; + search_params[AllJobsList.iterator + 'SearchSelectOpts'] = AllJobsList.fields.status.searchOptions; + search_params[AllJobsList.iterator + 'SearchFieldLabel'] = AllJobsList.fields.status.label.replace(//g,' '); + search_params[AllJobsList.iterator + 'SearchType'] = ''; + for (opt in AllJobsList.fields.status.searchOptions) { + if (AllJobsList.fields.status.searchOptions[opt].value === $routeParams.status) { + search_params[AllJobsList.iterator + 'SearchSelectValue'] = AllJobsList.fields.status.searchOptions[opt]; break; } } } - completed_scope = $scope.$new(true); - completed_scope.showJobType = true; + jobs_scope = $scope.$new(true); + jobs_scope.showJobType = true; LoadJobsScope({ parent_scope: $scope, - scope: completed_scope, - list: CompletedJobsList, - id: 'completed-jobs', - url: GetBasePath('unified_jobs') + '?or__status=successful&or__status=failed&or__status=error&or__status=canceled', - searchParams: search_params, - pageSize: max_rows - }); - running_scope = $scope.$new(true); - LoadJobsScope({ - parent_scope: $scope, - scope: running_scope, - list: RunningJobsList, + scope: jobs_scope, + list: AllJobsList, id: 'active-jobs', - url: GetBasePath('unified_jobs') + '?status=running', - pageSize: max_rows - }); - queued_scope = $scope.$new(true); - LoadJobsScope({ - parent_scope: $scope, - scope: queued_scope, - list: QueuedJobsList, - id: 'queued-jobs', - url: GetBasePath('unified_jobs') + '?or__status=pending&or__status=waiting&or__status=new', - pageSize: max_rows + url: GetBasePath('unified_jobs') + '?status__in=pending,running,completed,failed,successful,error,canceled', + pageSize: max_rows, + spinner: false }); scheduled_scope = $scope.$new(true); LoadSchedulesScope({ parent_scope: $scope, scope: scheduled_scope, list: ScheduledJobsList, - id: 'scheduled-jobs', + id: 'scheduled-jobs-tab', url: GetBasePath('schedules') + '?next_run__isnull=false', pageSize: max_rows }); $scope.refreshJobs = function() { - queued_scope.search('queued_job'); - running_scope.search('running_job'); - completed_scope.search('completed_job'); + jobs_scope.search('queued_job'); + jobs_scope.search('running_job'); + jobs_scope.search('completed_job'); + // queued_scope.search('queued_job'); + // running_scope.search('running_job'); + // completed_scope.search('completed_job'); scheduled_scope.search('schedule'); }; @@ -211,12 +169,12 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $routePa } $log.debug('available_height: ' + available_height); $('.jobs-list-container').each(function() { - $(this).height(Math.floor(available_height / 2)); + $(this).height(Math.floor(available_height)); }); search_row = Math.max($('.search-row:eq(0)').outerHeight(), 50); page_row = Math.max($('.page-row:eq(0)').outerHeight(), 33); - header = Math.max($('#completed_jobs_table thead').height(), 24); - height = Math.floor(available_height / 2) - header - page_row - search_row - 30; + header = Math.max($('#active_jobs_table thead').height(), 24); + height = Math.floor(available_height ) - header - page_row - search_row - 30; // row_height = (docw < 1350) ? 47 : 44; if(docw < 1424){ row_height = 55; @@ -240,16 +198,13 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $routePa // Set container height and return the number of allowed rows function resizeContainers() { setHeight(); - completed_scope[CompletedJobsList.iterator + '_page_size'] = max_rows; - completed_scope.changePageSize(CompletedJobsList.name, CompletedJobsList.iterator); - running_scope[RunningJobsList.iterator + '_page_size'] = max_rows; - running_scope.changePageSize(RunningJobsList.name, RunningJobsList.iterator); - queued_scope[QueuedJobsList.iterator + '_page_size'] = max_rows; - queued_scope.changePageSize(QueuedJobsList.name, QueuedJobsList.iterator); + jobs_scope[AllJobsList.iterator + '_page_size'] = max_rows; + jobs_scope.changePageSize(AllJobsList.name, AllJobsList.iterator); scheduled_scope[ScheduledJobsList.iterator + '_page_size'] = max_rows; scheduled_scope.changePageSize(ScheduledJobsList.name, ScheduledJobsList.iterator); } } -JobsListController.$inject = ['$rootScope', '$log', '$scope', '$compile', '$routeParams', 'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'LoadSchedulesScope', 'LoadJobsScope', 'RunningJobsList', 'CompletedJobsList', - 'QueuedJobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath', 'Wait', 'Socket']; +JobsListController.$inject = ['$rootScope', '$log', '$scope', '$compile', '$routeParams', +'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'LoadSchedulesScope', 'LoadJobsScope', +'AllJobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath', 'Wait', 'Socket']; diff --git a/awx/ui/static/js/filters.js b/awx/ui/static/js/filters.js index ecf681eeee..aaf9f04c17 100644 --- a/awx/ui/static/js/filters.js +++ b/awx/ui/static/js/filters.js @@ -1,5 +1,7 @@ -import sanitizeFilters from 'tower/filters/sanitize/xss-sanitizer.filter'; +import sanitizeFilter from 'tower/filters/sanitize/xss-sanitizer.filter'; +import capitalizeFilter from 'tower/filters/capitalize.filter'; export { - sanitizeFilters + sanitizeFilter, + capitalizeFilter }; diff --git a/awx/ui/static/js/filters/capitalize.filter.js b/awx/ui/static/js/filters/capitalize.filter.js new file mode 100644 index 0000000000..bbb392e0e4 --- /dev/null +++ b/awx/ui/static/js/filters/capitalize.filter.js @@ -0,0 +1,6 @@ +angular.module('capitalizeFilter', []).filter('capitalize', function() { + return function(input) { + input = input.charAt(0).toUpperCase() + input.substr(1).toLowerCase(); + return input; + }; +}); diff --git a/awx/ui/static/js/helpers/Jobs.js b/awx/ui/static/js/helpers/Jobs.js index 8f5c94a705..55192f3b79 100644 --- a/awx/ui/static/js/helpers/Jobs.js +++ b/awx/ui/static/js/helpers/Jobs.js @@ -28,8 +28,8 @@ export default function($location, Find, DeleteJob, RelaunchJob, LogViewer, $window) { return function(params) { var scope = params.scope, - iterator = (params.iterator) ? params.iterator : scope.iterator, - base = $location.path().replace(/^\//, '').split('/')[0]; + iterator = (params.iterator) ? params.iterator : scope.iterator; + //base = $location.path().replace(/^\//, '').split('/')[0]; scope.deleteJob = function(id) { DeleteJob({ scope: scope, id: id }); @@ -55,6 +55,9 @@ export default else if (scope.jobs) { list = scope.jobs; } + else if(scope.all_jobs){ + list = scope.all_jobs; + } job = Find({ list: list, key: 'id', val: id }); if (job.type === 'inventory_update') { typeId = job.inventory_source; @@ -69,9 +72,9 @@ export default }; scope.refreshJobs = function() { - if (base !== 'jobs') { + // if (base !== 'jobs') { scope.search(iterator); - } + // } }; @@ -89,6 +92,9 @@ export default else if (scope.jobs) { list = scope.jobs; } + else if(scope.all_jobs){ + list = scope.all_jobs; + } else if(scope.portal_jobs){ list=scope.portal_jobs; } diff --git a/awx/ui/static/js/lists.js b/awx/ui/static/js/lists.js index b47527e4e5..5cb576d745 100644 --- a/awx/ui/static/js/lists.js +++ b/awx/ui/static/js/lists.js @@ -1,6 +1,7 @@ import Admins from "tower/lists/Admins"; import CloudCredentials from "tower/lists/CloudCredentials"; import CompletedJobs from "tower/lists/CompletedJobs"; +import AllJobs from "tower/lists/AllJobs"; import ConfigureTowerJobs from "tower/lists/ConfigureTowerJobs"; import Credentials from "tower/lists/Credentials"; import CustomInventory from "tower/lists/CustomInventory"; @@ -33,6 +34,7 @@ export { Admins, CloudCredentials, CompletedJobs, + AllJobs, ConfigureTowerJobs, Credentials, CustomInventory, diff --git a/awx/ui/static/js/lists/AllJobs.js b/awx/ui/static/js/lists/AllJobs.js new file mode 100644 index 0000000000..ed708dfe27 --- /dev/null +++ b/awx/ui/static/js/lists/AllJobs.js @@ -0,0 +1,113 @@ +/********************************************* + * Copyright (c) 2014 AnsibleWorks, Inc. + * + * AllJobs.js + * + * + */ + + + +export default + angular.module('AllJobsDefinition', ['sanitizeFilter', 'capitalizeFilter']) + .value( 'AllJobsList', { + + name: 'all_jobs', + iterator: 'all_job', + editTitle: 'All Jobs', + index: false, + hover: true, + well: false, + + fields: { + id: { + label: 'ID', + ngClick:"viewJobLog(all_job.id)", + searchType: 'int', + columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2', + awToolTip: "{{ all_job.status_tip }}", + dataPlacement: 'top' + }, + status: { + label: 'Status', + columnClass: 'col-lg-2 col-md-2 col-sm-2 col-xs-2', + awToolTip: "{{ all_job.status_tip }}", + awTipPlacement: "top", + dataTitle: "{{ all_job.status_popover_title }}", + icon: 'icon-job-{{ all_job.status }}', + alt_text: "{{all_job.status | capitalize}}", + iconOnly: true, + ngClick:"viewJobLog(all_job.id)", + searchable: true, + searchType: 'select', + nosort: true, + searchOptions: [ + { name: "Success", value: "successful" }, + { name: "Error", value: "error" }, + { name: "Failed", value: "failed" }, + { name: "Canceled", value: "canceled" } + ] + }, + finished: { + label: 'Finished', + noLink: true, + searchable: false, + filter: "date:'MM/dd HH:mm:ss'", + columnClass: "col-lg-2 col-md-2 hidden-xs", + key: true, + desc: true + }, + type: { + label: 'Type', + ngBind: 'all_job.type_label', + link: false, + columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs", + columnShow: "showJobType", + searchable: true, + searchType: 'select', + searchOptions: [] // populated via GetChoices() in controller + }, + name: { + label: 'Name', + columnClass: 'col-md-3 col-sm-4 col-xs-4', + ngClick: "viewJobLog(all_job.id, all_job.nameHref)", + defaultSearchField: true, + awToolTip: "{{ all_job.name | sanitize }}", + dataPlacement: 'top' + }, + failed: { + label: 'Job failed?', + searchSingleValue: true, + searchType: 'boolean', + searchValue: 'true', + searchOnly: true, + nosort: true + } + }, + + actions: { }, + + fieldActions: { + submit: { + icon: 'icon-rocket', + mode: 'all', + ngClick: 'relaunchJob($event, all_job.id)', + awToolTip: 'Relaunch using the same parameters', + dataPlacement: 'top', + ngHide: "all_job.type == 'system_job' " + }, + "delete": { + mode: 'all', + ngClick: 'deleteJob(all_job.id)', + awToolTip: 'Delete the job', + dataPlacement: 'top' + }, + stdout: { + mode: 'all', + href: '/#/jobs/{{ all_job.id }}/stdout', + awToolTip: 'View standard output', + dataPlacement: 'top', + ngShow: "all_job.type == 'job'" + } + } + }); diff --git a/awx/ui/static/js/shared/generator-helpers.js b/awx/ui/static/js/shared/generator-helpers.js index 847ddd1b34..45f739db1c 100644 --- a/awx/ui/static/js/shared/generator-helpers.js +++ b/awx/ui/static/js/shared/generator-helpers.js @@ -481,6 +481,9 @@ angular.module('GeneratorHelpers', [systemStatus.name]) html += field.text; } html += ""; + if (field.alt_text) { + html += " " + field.alt_text; + } return html; }; }]) diff --git a/awx/ui/static/partials/jobs.html b/awx/ui/static/partials/jobs.html index 996b5ce790..ce5e2a6108 100644 --- a/awx/ui/static/partials/jobs.html +++ b/awx/ui/static/partials/jobs.html @@ -13,11 +13,11 @@ -
-
-
+ + +
+ +
+
+
+
+
-
-
+
+
+
-
-
-
-
-
-
-
Queued
-
-
-
-
-
+
-
-
-
-
Scheduled
-
-
-
-
-
-
-
-
+
+
-
\ No newline at end of file +