From 0fc977648e7402d11233fb49ad1232b5b3259f29 Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Wed, 18 Jun 2014 23:04:12 -0400 Subject: [PATCH] Job detail page refactor Improved handling of scrollbar refresh. Handling on via scope.$emit rather than inside the http response. Fixed pie chart drawing at job completion so that totaling of stats on playbook_on_stats event matches the way we're counting hosts during event processing. --- awx/ui/static/js/controllers/JobDetail.js | 111 +++++++++--- awx/ui/static/js/helpers/JobDetail.js | 200 +++++++++++++--------- awx/ui/static/partials/job_detail.html | 10 +- 3 files changed, 209 insertions(+), 112 deletions(-) diff --git a/awx/ui/static/js/controllers/JobDetail.js b/awx/ui/static/js/controllers/JobDetail.js index 32d016a8a2..44770df805 100644 --- a/awx/ui/static/js/controllers/JobDetail.js +++ b/awx/ui/static/js/controllers/JobDetail.js @@ -8,7 +8,7 @@ 'use strict'; function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, ClearScope, Breadcrumbs, LoadBreadCrumbs, GetBasePath, Wait, Rest, - ProcessErrors, ProcessEventQueue, SelectPlay, SelectTask, Socket, GetElapsed, SelectHost, FilterAllByHostName, DrawGraph, LoadHostSummary, ReloadHostSummaryList, + ProcessErrors, ProcessEventQueue, SelectPlay, SelectTask, Socket, GetElapsed, FilterAllByHostName, DrawGraph, LoadHostSummary, ReloadHostSummaryList, JobIsFinished, SetTaskStyles) { ClearScope(); @@ -31,10 +31,10 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, scope.hostResultsMap = {}; api_complete = false; - scope.hostTableRows = 150; - scope.hostSummaryTableRows = 150; - scope.tasksMaxRows = 150; - scope.playsMaxRows = 150; + scope.hostTableRows = 75; + scope.hostSummaryTableRows = 75; + scope.tasksMaxRows = 75; + scope.playsMaxRows = 75; scope.search_all_tasks = []; scope.search_all_plays = []; @@ -188,8 +188,11 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, scope.removeRefreshJobDetails = scope.$on('LoadJobDetails', function(e, events_url) { // Call to load all the job bits including, plays, tasks, hosts results and host summary - scope.plays = []; - scope.playsMap = {}; + scope.host_summary.ok = 0; + scope.host_summary.changed = 0; + scope.host_summary.unreachable = 0; + scope.host_summary.failed = 0; + scope.host_summary.total = 0; var url = scope.job.url + 'job_plays/?order_by=id'; Rest.setUrl(url); @@ -198,7 +201,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, data.forEach(function(event, idx) { var status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful', start = event.started, - end, elapsed; + end, elapsed, play; if (idx < data.length - 1) { // end date = starting date of the next event @@ -218,18 +221,29 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, elapsed = '00:00:00'; } - scope.plays.push({ - id: event.id, - name: event.play, - created: start, - finished: end, - status: status, - elapsed: elapsed, - playActiveClass: '', - hostCount: 0, - fistTask: null - }); - scope.playsMap[event.id] = scope.plays.length - 1; + if (scope.playsMap[event.id] !== undefined) { + play = scope.plays[scope.playsMap[event.id]]; + play.finished = end; + play.status = status; + play.elapsed = elapsed; + play.playActiveClass = ''; + } + else { + scope.plays.push({ + id: event.id, + name: event.play, + created: start, + finished: end, + status: status, + elapsed: elapsed, + playActiveClass: '', + hostCount: 0, + fistTask: null + }); + if (scope.plays.length > scope.playsMaxRows) { + scope.plays.shift(); + } + } scope.host_summary.ok += (data.ok_count) ? data.ok_count : 0; scope.host_summary.changed += (data.changed_count) ? data.changed_count : 0; @@ -239,7 +253,14 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, scope.host_summary.unreachable + scope.host_summary.failed; }); + //rebuild the index + scope.playsMap = {}; + scope.plays.forEach(function(play, idx) { + scope.playsMap[play.id] = idx; + }); + scope.$emit('PlaysReady', events_url); + scope.$emit('FixPlaysScroll'); }) .error( function(data, status) { ProcessErrors(scope, data, status, null, { hdr: 'Error!', @@ -334,6 +355,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, }); }); + if (scope.removeRefreshCompleted) { scope.removeRefreshCompleted(); } @@ -349,6 +371,41 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, } }); + if (scope.removeFixPlaysScroll) { + scope.removeFixPlaysScroll(); + } + scope.removeFixPlaysScroll = scope.$on('FixPlaysScroll', function() { + $('#plays-table-detail').mCustomScrollbar("update"); + setTimeout( function() { + scope.auto_scroll = true; + $('#tasks-table-detail').mCustomScrollbar("scrollTo", "bottom"); + }, 500); + }); + + if (scope.removeFixTasksScroll) { + scope.removeFixTasksScroll(); + } + scope.removeFixTasksScroll = scope.$on('FixTasksScroll', function() { + $('#tasks-table-detail').mCustomScrollbar("update"); + setTimeout( function() { + scope.auto_scroll = true; + $('#tasks-table-detail').mCustomScrollbar("scrollTo", "bottom"); + }, 500); + }); + + + if (scope.removeFixHostResultsScroll) { + scope.removeFixHostResultsScroll(); + } + scope.removeFixHostResultsScroll = scope.$on('FixHostResultsScroll', function() { + $('#hosts-table-detail').mCustomScrollbar("update"); + setTimeout( function() { + scope.auto_scroll = true; + $('#hosts-table-detail').mCustomScrollbar("scrollTo", "bottom"); + }, 500); + }); + + scope.adjustSize = function() { var height, ww = $(window).width(); if (ww < 1240) { @@ -512,7 +569,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, url = GetBasePath('jobs') + job_id + '/job_events/?parent=' + scope.activeTask + '&'; url += (scope.search_all_hosts_name) ? 'host__name__icontains=' + scope.search_all_hosts_name + '&' : ''; url += (scope.searchAllStatus === 'failed') ? 'failed=true&' : ''; - url += 'host__name__gt=' + scope.hostResults[scope.hostResults.length - 1].name + '&host__isnull=false&page_size=' + (scope.hostTableRows / 3) + '&order_by=host__name'; + url += 'host__name__gt=' + scope.hostResults[scope.hostResults.length - 1].name + '&host__isnull=false&page_size=' + scope.hostTableRows + '&order_by=host__name'; Wait('start'); Rest.setUrl(url); Rest.get() @@ -559,7 +616,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, url = GetBasePath('jobs') + job_id + '/job_events/?parent=' + scope.activeTask + '&'; url += (scope.search_all_hosts_name) ? 'host__name__icontains=' + scope.search_all_hosts_name + '&' : ''; url += (scope.searchAllStatus === 'failed') ? 'failed=true&' : ''; - url += 'host__name__lt=' + scope.hostResults[0].name + '&host__isnull=false&page_size=' + (scope.hostTableRows / 3) + '&order_by=-host__name'; + url += 'host__name__lt=' + scope.hostResults[0].name + '&host__isnull=false&page_size=' + scope.hostTableRows + '&order_by=-host__name'; Wait('start'); Rest.setUrl(url); Rest.get() @@ -606,7 +663,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, url = scope.job.url + 'job_tasks/?event_id=' + scope.activePlay; url += (scope.search_all_tasks.length > 0) ? '&id__in=' + scope.search_all_tasks.join() : ''; url += (scope.searchAllStatus === 'failed') ? '&failed=true' : ''; - url += '&id__gt=' + scope.tasks[scope.tasks.length - 1].id + '&page_size=' + (scope.tasksMaxRows / 3) + '&order_by=id'; + url += '&id__gt=' + scope.tasks[scope.tasks.length - 1].id + '&page_size=' + scope.tasksMaxRows + '&order_by=id'; Wait('start'); Rest.setUrl(url); Rest.get() @@ -684,7 +741,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, url = scope.job.url + 'job_tasks/?event_id=' + scope.activePlay; url += (scope.search_all_tasks.length > 0) ? '&id__in=' + scope.search_all_tasks.join() : ''; url += (scope.searchAllStatus === 'failed') ? '&failed=true' : ''; - url += '&id__lt=' + scope.tasks[scope.tasks[0]].name + '&page_size=' + (scope.tasksMaxRows / 3) + '&order_by=id'; + url += '&id__lt=' + scope.tasks[scope.tasks[0]].id + '&page_size=' + scope.tasksMaxRows + '&order_by=id'; Wait('start'); Rest.setUrl(url); Rest.get() @@ -759,7 +816,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, url = GetBasePath('jobs') + job_id + '/job_host_summaries/?'; url += (scope.search_all_hosts_name) ? 'host__name__icontains=' + scope.search_all_hosts_name + '&' : ''; url += (scope.searchAllStatus === 'failed') ? 'failed=true&' : ''; - url += 'host__name__gt=' + scope.hosts[scope.hosts.length - 1].name + '&page_size=' + (scope.hostSummaryTableRows / 3) + '&order_by=host__name'; + url += 'host__name__gt=' + scope.hosts[scope.hosts.length - 1].name + '&page_size=' + scope.hostSummaryTableRows + '&order_by=host__name'; Wait('start'); Rest.setUrl(url); Rest.get() @@ -803,7 +860,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, url = GetBasePath('jobs') + job_id + '/job_host_summaries/?'; url += (scope.search_all_hosts_name) ? 'host__name__icontains=' + scope.search_all_hosts_name + '&' : ''; url += (scope.searchAllStatus === 'failed') ? 'failed=true&' : ''; - url += 'host__name__lt=' + scope.hosts[0].name + '&page_size=' + (scope.hostSummaryTableRows / 3) + '&order_by=-host__name'; + url += 'host__name__lt=' + scope.hosts[0].name + '&page_size=' + scope.hostSummaryTableRows + '&order_by=-host__name'; Wait('start'); Rest.setUrl(url); Rest.get() @@ -904,6 +961,6 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, } JobDetailController.$inject = [ '$rootScope', '$scope', '$compile', '$routeParams', '$log', 'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'GetBasePath', - 'Wait', 'Rest', 'ProcessErrors', 'ProcessEventQueue', 'SelectPlay', 'SelectTask', 'Socket', 'GetElapsed', 'SelectHost', 'FilterAllByHostName', 'DrawGraph', + 'Wait', 'Rest', 'ProcessErrors', 'ProcessEventQueue', 'SelectPlay', 'SelectTask', 'Socket', 'GetElapsed', 'FilterAllByHostName', 'DrawGraph', 'LoadHostSummary', 'ReloadHostSummaryList', 'JobIsFinished', 'SetTaskStyles' ]; diff --git a/awx/ui/static/js/helpers/JobDetail.js b/awx/ui/static/js/helpers/JobDetail.js index 3d65224989..9af481396e 100644 --- a/awx/ui/static/js/helpers/JobDetail.js +++ b/awx/ui/static/js/helpers/JobDetail.js @@ -88,7 +88,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge fistTask: null }); scope.playsMap[event.id] = scope.plays.length -1; - if (scope.activePlay && scope.playsMap[scope.activePlay] !== undefined) { + if (scope.playsMap[scope.activePlay] !== undefined) { scope.plays[scope.playsMap[scope.activePlay]].playActiveClass = ''; } scope.activePlay = event.id; @@ -99,6 +99,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge scope.hostResultsMap = {}; $('#hosts-table-detail').mCustomScrollbar("update"); $('#tasks-table-detail').mCustomScrollbar("update"); + scope.$emit('FixPlaysScroll'); break; case 'playbook_on_setup': @@ -280,12 +281,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge scope.hasRoles = (event.role) ? true : false; $('#hosts-table-detail').mCustomScrollbar("update"); - $('#tasks-table-detail').mCustomScrollbar("update"); - setTimeout( function() { - scope.auto_scroll = true; - $('#tasks-table-detail').mCustomScrollbar("scrollTo", "bottom"); - - }, 1500); + scope.$emit('FixTasksScroll'); // Record the first task id UpdatePlayStatus({ @@ -340,7 +336,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge status_text = params.status_text, play; - if (scope.playsMap[id]) { + if (scope.playsMap[id] !== undefined) { play = scope.plays[scope.playsMap[id]]; if (failed) { play.status = 'failed'; @@ -380,7 +376,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge no_hosts = params.no_hosts, task; - if (scope.tasksMap[id]) { + if (scope.tasksMap[id] !== undefined) { task = scope.tasks[scope.tasksMap[id]]; if (no_hosts){ task.status = 'no-matching-hosts'; @@ -582,14 +578,15 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge var scope = params.scope, id = params.id, callback = params.callback, - clear = true; + clear = false; // Determine if the tasks and hostResults arrays should be initialized - //if (scope.search_all_hosts_name || scope.searchAllStatus === 'failed') { - // clear = true; - //} - //else { - // clear = (scope.activePlay === id) ? false : true; //are we moving to a new play? + if (scope.search_all_hosts_name || scope.searchAllStatus === 'failed') { + clear = true; + } + else { + clear = (scope.activePlay === id) ? false : true; //are we moving to a new play? + } if (scope.activePlay && scope.playsMap[scope.activePlay] !== undefined) { scope.plays[scope.playsMap[scope.activePlay]].playActiveClass = ''; @@ -616,10 +613,13 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge return function(params) { var scope = params.scope, callback = params.callback, + clear = params.clear, url; - scope.tasks = []; - scope.tasksMap = {}; + if (clear) { + scope.tasks = []; + scope.tasksMap = {}; + } if (scope.activePlay) { url = scope.job.url + 'job_tasks/?event_id=' + scope.activePlay; @@ -631,7 +631,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge Rest.get() .success(function(data) { data.results.forEach(function(event, idx) { - var end, elapsed; + var task, end, elapsed; if (!scope.plays[scope.playsMap[scope.activePlay]].firstTask) { scope.plays[scope.playsMap[scope.activePlay]].firstTask = event.id; @@ -657,36 +657,64 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge elapsed = '00:00:00'; } - scope.tasks.push({ - id: event.id, - play_id: scope.activePlay, - name: event.name, - status: ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ), - created: event.created, - modified: event.modified, - finished: end, - elapsed: elapsed, - hostCount: (event.host_count) ? event.host_count : 0, - reportedHosts: (event.reported_hosts) ? event.reported_hosts : 0, - successfulCount: (event.successful_count) ? event.successful_count : 0, - failedCount: (event.failed_count) ? event.failed_count : 0, - changedCount: (event.changed_count) ? event.changed_count : 0, - skippedCount: (event.skipped_count) ? event.skipped_count : 0, - taskActiveClass: '' - }); - scope.tasksMap[event.id] = scope.tasks.length - 1; + if (scope.tasksMap[event.id] !== undefined) { + task = scope.tasks[scope.tasksMap[event.id]]; + task.status = ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ); + task.modified = event.modified; + task.finished = end; + task.elapsed = elapsed; + task.hostCount = (event.host_count) ? event.host_count : 0; + task.reportedHosts = (event.reported_hosts) ? event.reported_hosts : 0; + task.successfulCount = (event.successful_count) ? event.successful_count : 0; + task.failedCount = (event.failed_count) ? event.failed_count : 0; + task.changedCount = (event.changed_count) ? event.changed_count : 0; + task.skippedCount = (event.skipped_count) ? event.skipped_count : 0; + task.taskActiveClass = ''; + } + else { + scope.tasks.push({ + id: event.id, + play_id: scope.activePlay, + name: event.name, + status: ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ), + created: event.created, + modified: event.modified, + finished: end, + elapsed: elapsed, + hostCount: (event.host_count) ? event.host_count : 0, + reportedHosts: (event.reported_hosts) ? event.reported_hosts : 0, + successfulCount: (event.successful_count) ? event.successful_count : 0, + failedCount: (event.failed_count) ? event.failed_count : 0, + changedCount: (event.changed_count) ? event.changed_count : 0, + skippedCount: (event.skipped_count) ? event.skipped_count : 0, + taskActiveClass: '' + }); + scope.tasksMap[event.id] = scope.tasks.length - 1; + if (scope.tasks.length > scope.tasksMaxRows) { + scope.tasks.shift(); + } + } SetTaskStyles({ scope: scope, task_id: event.id }); }); + //rebuild the index; + scope.tasksMap = {}; + scope.tasks.forEach(function(task, idx) { + scope.tasksMap[task.id] = idx; + }); + // set the active task SelectTask({ scope: scope, id: (scope.tasks.length > 0) ? scope.tasks[scope.tasks.length - 1].id : null, callback: callback }); + + scope.$emit('FixTasksScroll'); + }) .error(function(data) { ProcessErrors(scope, data, status, null, { hdr: 'Error!', @@ -696,6 +724,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge else { scope.tasks = []; scope.tasksMap = {}; + $('#tasks-table-detail').mCustomScrollbar("update"); SelectTask({ scope: scope, id: null, @@ -711,14 +740,14 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge var scope = params.scope, id = params.id, callback = params.callback, - clear=true; + clear=false; - //if (scope.search_all_hosts_name || scope.searchAllStatus === 'failed') { - // clear = true; - //} - //else { - // clear = (scope.activeTask === id) ? false : true; - //} + if (scope.search_all_hosts_name || scope.searchAllStatus === 'failed') { + clear = true; + } + else { + clear = (scope.activeTask === id) ? false : true; + } if (scope.activeTask && scope.tasksMap[scope.activeTask] !== undefined) { scope.tasks[scope.tasksMap[scope.activeTask]].taskActiveClass = ''; @@ -728,13 +757,6 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge } scope.activeTask = id; - $('#tasks-table-detail').mCustomScrollbar("update"); - setTimeout( function() { - scope.auto_scroll = true; - $('#tasks-table-detail').mCustomScrollbar("scrollTo", "bottom"); - - }, 1500); - LoadHosts({ scope: scope, callback: callback, @@ -744,7 +766,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge }]) // Refresh the list of hosts -.factory('LoadHosts', ['Rest', 'ProcessErrors', 'SelectHost', function(Rest, ProcessErrors, SelectHost) { +.factory('LoadHosts', ['Rest', 'ProcessErrors', function(Rest, ProcessErrors) { return function(params) { var scope = params.scope, callback = params.callback, @@ -766,21 +788,39 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge Rest.get() .success(function(data) { data.results.forEach(function(event) { - scope.hostResults.push({ - id: event.id, - status: ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ), - host_id: event.host, - task_id: event.parent, - name: event.event_data.host, - created: event.created, - msg: ( (event.event_data && event.event_data.res) ? event.event_data.res.msg : '' ) - }); - scope.hostResultsMap[event.id] = scope.hostResults.length - 1; + var result; + if (scope.hostResultsMap[event.id] !== undefined) { + result = scope.hostResults[scope.hostResultsMap[event.id]]; + result.status = ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ); + result.created = event.created; + result.msg = (event.event_data && event.event_data.res) ? event.event_data.res.msg : ''; + } + else { + scope.hostResults.push({ + id: event.id, + status: ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ), + host_id: event.host, + task_id: event.parent, + name: event.event_data.host, + created: event.created, + msg: ( (event.event_data && event.event_data.res) ? event.event_data.res.msg : '' ) + }); + if (scope.hostResults.length > scope.hostTableRows) { + scope.hostResults.shift(); + } + } }); + + // Rebuild the index + scope.hostResultsMap = {}; + scope.hostResults.forEach(function(result, idx) { + scope.hostResultsMap[result.id] = idx; + }); + if (callback) { scope.$emit(callback); } - SelectHost({ scope: scope }); + scope.$emit('FixHostResultsScroll'); }) .error(function(data, status) { ProcessErrors(scope, data, status, null, { hdr: 'Error!', @@ -793,22 +833,11 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge if (callback) { scope.$emit(callback); } - SelectHost({ scope: scope }); + $('#hosts-table-detail').mCustomScrollbar("update"); } }; }]) -.factory('SelectHost', [ function() { - return function(params) { - var scope = params.scope; - $('#hosts-table-detail').mCustomScrollbar("update"); - setTimeout( function() { - scope.auto_scroll = true; - $('#hosts-table-detail').mCustomScrollbar("scrollTo", "bottom"); - }, 700); - }; -}]) - // Refresh the list of hosts in the hosts summary section .factory('ReloadHostSummaryList', ['Rest', 'ProcessErrors', function(Rest, ProcessErrors) { return function(params) { @@ -864,11 +893,24 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge .factory('LoadHostSummary', [ function() { return function(params) { var scope = params.scope, - data = params.data; - scope.host_summary.ok = Object.keys(data.ok).length; - scope.host_summary.changed = Object.keys(data.changed).length; - scope.host_summary.unreachable = Object.keys(data.dark).length; - scope.host_summary.failed = Object.keys(data.failures).length; + data = params.data, + host; + scope.host_summary.ok = 0; + for (host in data.ok) { + scope.host_summary.ok += data.ok[host]; + } + scope.host_summary.changed = 0; + for (host in data.changed) { + scope.host_summary.changed += data.changed[host]; + } + scope.host_summary.unreachable = 0; + for (host in data.dark) { + scope.host_summary.dark += data.dark[host]; + } + scope.host_summary.failed = 0; + for (host in data.failures) { + scope.host_summary.failed += data.failures[host]; + } scope.host_summary.total = scope.host_summary.ok + scope.host_summary.changed + scope.host_summary.unreachable + scope.host_summary.failed; }; diff --git a/awx/ui/static/partials/job_detail.html b/awx/ui/static/partials/job_detail.html index a70e2d4152..40525d2e36 100644 --- a/awx/ui/static/partials/job_detail.html +++ b/awx/ui/static/partials/job_detail.html @@ -3,12 +3,10 @@