diff --git a/awx/ui/client/features/templates/list-templates.controller.js b/awx/ui/client/features/templates/list-templates.controller.js index a85a71204b..e25bdf7de2 100644 --- a/awx/ui/client/features/templates/list-templates.controller.js +++ b/awx/ui/client/features/templates/list-templates.controller.js @@ -34,6 +34,15 @@ function ListTemplatesController( vm.strings = strings; vm.templateTypes = mapChoices(choices); vm.activeId = parseInt($state.params.job_template_id || $state.params.workflow_template_id); + vm.invalidTooltip = { + popover: { + text: strings.get('error.INVALID'), + on: 'mouseenter', + icon: 'fa-exclamation', + position: 'right', + arrowHeight: 15 + } + } $scope.canAddJobTemplate = jobTemplate.options('actions.POST'); $scope.canAddWorkflowJobTemplate = workflowTemplate.options('actions.POST'); @@ -53,6 +62,14 @@ function ListTemplatesController( $scope[name] = dataset.results; }); + vm.isInvalid = (template) => { + if(isJobTemplate(template)) { + return (template.inventory === null || template.project == null) + } else { + return false; + } + }; + vm.runTemplate = template => { if (!template) { Alert(strings.get('error.LAUNCH'), strings.get('alert.MISSING_PARAMETER')); diff --git a/awx/ui/client/features/templates/list.view.html b/awx/ui/client/features/templates/list.view.html index 7759287085..c6d2e19db0 100644 --- a/awx/ui/client/features/templates/list.view.html +++ b/awx/ui/client/features/templates/list.view.html @@ -50,6 +50,9 @@ +
+ +
:first-child { - padding-left: 10px; -} - .List-tableRow--disabled { .List-tableCell, .List-tableCell * { color: @b7grey; @@ -98,6 +94,25 @@ table, tbody { } } +.List-tableRow--invalid { + height: 40px; +} + +.List-tableRow--invalidBar { + align-items: center; + border-left: solid @at-space-2x @at-color-error; + color: @at-white; + display: flex; + height: 100%; + justify-content: center; + position: relative; + + i { + position: absolute; + left: -7px; + } +} + .List-tableCell { padding: 7px 15px; border-top:0px!important; @@ -387,6 +402,11 @@ table, tbody { max-width: 164px; } +.List-staticColumn--invalidBar { + width: 10px; + padding-right: 0px!important; +} + .List-staticColumnAdjacent { padding-left: 10px!important; } diff --git a/awx/ui/client/lib/components/list/_index.less b/awx/ui/client/lib/components/list/_index.less index a4daa3248b..0deaa7c250 100644 --- a/awx/ui/client/lib/components/list/_index.less +++ b/awx/ui/client/lib/components/list/_index.less @@ -72,6 +72,7 @@ justify-content: space-between; align-items: center; padding: @at-padding-list-row; + position: relative; } .at-Row--active { @@ -80,6 +81,29 @@ border-top-right-radius: @at-border-radius; } +.at-Row--invalid { + align-items: center; + background: @at-color-error; + display: flex; + height: 100%; + justify-content: center; + left: 0; + position: absolute; + width: @at-space-2x; + + .at-Popover { + padding: 0; + + &-icon i { + color: @at-white; + } + + &-icon i:hover { + color: @at-white; + } + } +} + .at-Row ~ .at-Row { border-top-left-radius: 0px; border-top-right-radius: 0px; diff --git a/awx/ui/client/src/scheduler/schedulerList.controller.js b/awx/ui/client/src/scheduler/schedulerList.controller.js index c0ad965239..3ae664216a 100644 --- a/awx/ui/client/src/scheduler/schedulerList.controller.js +++ b/awx/ui/client/src/scheduler/schedulerList.controller.js @@ -46,6 +46,20 @@ export default [ // _.forEach($scope[list.name], buildTooltips); } + $scope.isValid = (schedule) => { + let type = schedule.summary_fields.unified_job_template.unified_job_type; + switch(type){ + case 'job': + return _.every(['project', 'inventory'], _.partial(_.has, schedule.related)); + case 'project_update': + return _.has(schedule, 'related.project'); + case 'inventory_update': + return _.has(schedule, 'related.inventory'); + default: + return true; + } + }; + $scope.$on(`${list.iterator}_options`, function(event, data){ $scope.options = data.data.actions.GET; optionsRequestDataProcessing(); diff --git a/awx/ui/client/src/scheduler/schedules.list.js b/awx/ui/client/src/scheduler/schedules.list.js index b6fbf4b811..70f785ecdf 100644 --- a/awx/ui/client/src/scheduler/schedules.list.js +++ b/awx/ui/client/src/scheduler/schedules.list.js @@ -4,7 +4,6 @@ * All Rights Reserved *************************************************/ - export default ['i18n', function(i18n) { return { @@ -17,6 +16,15 @@ export default ['i18n', function(i18n) { hover: true, fields: { + invalid: { + columnClass: "List-staticColumn--invalidBar", + label: '', + type: 'invalid', + nosort: true, + awToolTip: i18n._("Resources are missing from this template."), + dataPlacement: 'right', + ngShow: '!isValid(schedule)' + }, toggleSchedule: { ngDisabled: "!schedule.summary_fields.user_capabilities.edit", label: '', diff --git a/awx/ui/client/src/shared/generator-helpers.js b/awx/ui/client/src/shared/generator-helpers.js index f2fd9f2f1f..f5ab269ac7 100644 --- a/awx/ui/client/src/shared/generator-helpers.js +++ b/awx/ui/client/src/shared/generator-helpers.js @@ -549,6 +549,11 @@ angular.module('GeneratorHelpers', [systemStatus.name]) html += "ng-show='!" + list.iterator + "." ; html += (field.flag) ? field.flag : "enabled"; html += "' class='ScheduleToggle-switch' ng-click='" + field.ngClick + "'>" + i18n._("OFF") + "
"; + } else if (field.type === 'invalid') { + html += `
`; + html += ""; + html += "
"; } else { html += "