Create/edit survey re-work

This commit is contained in:
Michael Abashian 2016-04-07 15:11:07 -04:00
parent 2d31021296
commit 0f2d924adb
29 changed files with 929 additions and 759 deletions

View File

@ -2058,7 +2058,8 @@ tr td button i {
margin-top: 10px;
}
.select2-container--disabled,.select2-container--disabled .select2-selection--single{
/* Overwrite select2 base styles for single/multiple selects so that match up with other form elements. Also overwrite disabled styles. */
.select2-container--disabled,.select2-container--disabled .select2-selection--single,.select2-container--disabled .select2-selection--multiple {
cursor: not-allowed !important;
}
@ -2066,6 +2067,19 @@ tr td button i {
opacity: .35;
}
.select2-container--default .select2-selection--single {
background-color: @field-secondary-bg;
border: 1px solid #aaa;
border-radius: 4px;
}
.select2-container--default .select2-selection--multiple {
background-color: @field-secondary-bg;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text;
}
body.is-modalOpen {
overflow: hidden;
}

View File

@ -30,6 +30,12 @@
min-height: 45px;
}
.Form-secondaryTitle{
color: @default-icon;
padding-bottom: 20px;
min-height: 40px;
}
.Form-title--is_superuser{
height:15px;
color: @btn-txt;
@ -136,7 +142,7 @@
.Form-formGroup {
flex: 1 0 auto;
margin-bottom: 25px;
margin-bottom: 20px;
width: 33%;
padding-right: 50px;
}
@ -226,7 +232,6 @@
.Form-dropDown {
height: 30px !important;
background-color: @field-secondary-bg!important;
border-radius: 5px !important;
border:1px solid @field-border!important;
color: @field-input-text!important;
@ -431,6 +436,25 @@ input[type='radio']:checked:before {
color: @btn-txt;
}
.Form-surveyButton {
background-color: @default-link;
margin-right: 20px;
color: @default-bg;
text-transform: uppercase;
padding-left:15px;
padding-right: 15px;
}
.Form-surveyButton:hover{
background-color: @default-link-hov;
color: @default-bg;
}
.Form-formGroup--singleColumn {
width: 100%;
padding-right: 0px;
}
@media only screen and (max-width: 650px) {
.Form-formGroup {
flex: 1 0 auto;

View File

@ -28,8 +28,15 @@ table.ui-datepicker-calendar {
font-weight: bold;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1;
opacity: .7;
opacity: 1;
text-shadow: 0 1px 0 @white;
color:@default-second-border;
}
.close:hover{
color:@default-icon;
}
.ui-widget {
font-family: 'Open Sans', sans-serif;
}
.ui-widget-header {
border-radius: 0;

View File

@ -49,7 +49,6 @@ export default
variables: {
label: 'Variables',
type: 'textarea',
'class': 'Form-textArea',
addRequired: false,
editRequird: false,
rows: 6,

View File

@ -276,8 +276,8 @@ export default
type: 'custom',
column: 2,
ngHide: "job_type.value === 'scan'" ,
control: '<button type="button" class="btn btn-sm Form-buttonDefault" id="job_templates_create_survey_btn" ng-show="!survey_exists" ng-click="addSurvey()">ADD SURVEY</button>'+
'<button type="button" class="btn btn-sm Form-buttonDefault" id="job_templates_edit_survey_btn" ng-show="survey_exists" ng-click="editSurvey()">EDIT SURVEY</button>'
control: '<button type="button" class="btn btn-sm Form-surveyButton" id="job_templates_create_survey_btn" ng-show="!survey_exists" ng-click="addSurvey()">ADD SURVEY</button>'+
'<button type="button" class="btn btn-sm Form-surveyButton" id="job_templates_edit_survey_btn" ng-show="survey_exists" ng-click="editSurvey()">EDIT SURVEY</button>'
}
},

View File

@ -78,7 +78,7 @@ angular.module('JobTemplatesHelper', ['Utilities'])
.success(function (data) {
var fld, i;
for (fld in form.fields) {
if (fld !== 'variables' && data[fld] !== null && data[fld] !== undefined) {
if (fld !== 'variables' && fld !== 'survey' && data[fld] !== null && data[fld] !== undefined) {
if (form.fields[fld].type === 'select') {
if (scope[fld + '_options'] && scope[fld + '_options'].length > 0) {
for (i = 0; i < scope[fld + '_options'].length; i++) {
@ -91,10 +91,8 @@ angular.module('JobTemplatesHelper', ['Utilities'])
}
} else {
scope[fld] = data[fld];
if(fld ==='survey_enabled'){
if(!Empty(data.summary_fields.survey)) {
scope.survey_exists = true;
}
if(!Empty(data.summary_fields.survey)) {
scope.survey_exists = true;
}
}
master[fld] = scope[fld];
@ -114,6 +112,8 @@ angular.module('JobTemplatesHelper', ['Utilities'])
Wait('stop');
scope.url = data.url;
scope.survey_enabled = data.survey_enabled;
scope.ask_variables_on_launch = (data.ask_variables_on_launch) ? 'true' : 'false';
master.ask_variables_on_launch = scope.ask_variables_on_launch;

View File

@ -337,16 +337,20 @@
if (form.fields[fld].type === 'select' && fld !== 'playbook') {
data[fld] = $scope[fld].value;
} else {
if (fld !== 'variables') {
if (fld !== 'variables' && fld !== 'survey') {
data[fld] = $scope[fld];
}
}
}
data.extra_vars = ToJSON($scope.parseType, $scope.variables, true);
if(data.job_type === 'scan' && $scope.default_scan === true){
data.project = "";
data.playbook = "";
data.project = "";
data.playbook = "";
}
// We only want to set the survey_enabled flag to true for this job template if a survey exists
// and it's been enabled. By default, survey_enabled is explicitly set to true but if no survey
// is created then we don't want it enabled.
data.survey_enabled = ($scope.survey_enabled && $scope.survey_exists) ? $scope.survey_enabled : false;
Rest.setUrl(defaultUrl);
Rest.post(data)
.success(function(data) {
@ -361,7 +365,7 @@
url: $scope.current_url
});
if(data.survey_enabled===true){
if($scope.survey_questions && $scope.survey_questions.length > 0){
//once the job template information is saved we submit the survey info to the correct endpoint
var url = data.url+ 'survey_spec/';
Rest.setUrl(url);
@ -413,19 +417,12 @@
if($scope.job_type.value === "scan" && $scope.survey_enabled === true){
$scope.survey_enabled = false;
}
// Can't have a survey enabled without a survey
if($scope.survey_enabled === true && $scope.survey_exists!==true){
// $scope.$emit("PromptForSurvey");
// The original design for this was a pop up that would prompt the user if they wanted to create a
// survey, because they had enabled one but not created it yet. We switched this for now so that
// an error message would be displayed by the survey buttons that tells the user to add a survey or disabled
// surveys.
$scope.invalid_survey = true;
return;
} else {
$scope.$emit("GatherFormFields");
$scope.survey_enabled = false;
}
$scope.$emit("GatherFormFields");
};

View File

@ -1,5 +1,5 @@
<div class="tab-pane" id="job_templates_create">
<div ui-view></div>
<div ng-cloak id="htmlTemplate" class="Panel"></div>
<div id="survey-modal-dialog"></div>
<div ng-include="'/static/partials/survey-maker-modal.html'"></div>
</div>

View File

@ -307,27 +307,6 @@ export default
Wait('start');
if ($scope.removeEnableSurvey) {
$scope.removeEnableSurvey();
}
$scope.removeEnableSurvey = $scope.$on('EnableSurvey', function(fld) {
$('#job_templates_survey_enabled_chbox').attr('checked', $scope[fld]);
Rest.setUrl(defaultUrl + id+ '/survey_spec/');
Rest.get()
.success(function (data) {
if(data && data.name){
$scope.survey_exists = true;
}
})
.error(function (data, status) {
ProcessErrors($scope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to retrieve job template: ' + $stateParams.template_id + '. GET status: ' + status
});
});
});
if ($scope.removeSurveySaved) {
$scope.rmoveSurveySaved();
}
@ -506,7 +485,7 @@ export default
.error(function(res, status){
ProcessErrors($rootScope, res, status, null, {hdr: 'Error!',
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
});
});
}
else {
$state.go('jobTemplates');

View File

@ -1,5 +1,5 @@
<div class="tab-pane" id="job_templates_edit">
<div ui-view></div>
<div ng-cloak id="htmlTemplate" class="Panel"></div>
<div id="survey-modal-dialog"></div>
<div ng-include="'/static/partials/survey-maker-modal.html'"></div>
</div>

View File

@ -1,5 +1,5 @@
export default
function EditQuestion(GetBasePath, Rest, Wait, ProcessErrors, $compile, GenerateForm, SurveyQuestionForm) {
function EditQuestion(GetBasePath, Rest, Wait, ProcessErrors, $compile, GenerateForm, SurveyQuestionForm, CreateSelect2) {
return function(params) {
var scope = params.scope,
@ -7,16 +7,12 @@ export default
element,
tmpVar,
i,
question = params.question, //scope.survey_questions[index],
question = params.question,
form = SurveyQuestionForm;
$('#survey-save-button').attr('disabled', 'disabled');
angular.element('#survey_question_question_cancel_btn').trigger('click');
$('#add_question_btn').hide();
// $('#new_question .aw-form-well').remove();
element = $('.question_final:eq('+index+')');
element.css('opacity', 1.0);
element.empty();
// Update the index so that we know which question is being edited.
scope.editQuestionIndex = index;
scope.text_min = null;
scope.text_max = null;
scope.int_min = null;
@ -70,6 +66,16 @@ export default
else if ( question.type === 'multiselect'){
scope.default_multiselect = question.default;
}
// After we populate the form with data, need to call CreateSelect2 again
// to get the dropdown to show the selected item.
CreateSelect2({
element:'#survey_question_type',
multiple: false
});
// Set the form to dirty. This lets the cancel button know that it should become enabled.
scope.survey_question_form.$setDirty();
});
if (scope.removeGenerateForm) {
@ -77,7 +83,7 @@ export default
}
scope.removeGenerateForm = scope.$on('GenerateForm', function() {
tmpVar = scope.mode;
GenerateForm.inject(form, { id: 'question_'+index, mode: 'edit' , related: false, scope:scope });
GenerateForm.inject(form, { id: 'survey_maker_question_form', mode: 'edit', related: false, scope:scope });
scope.mode = tmpVar;
scope.$emit('FillQuestionForm');
});
@ -95,5 +101,6 @@ EditQuestion.$inject =
'ProcessErrors',
'$compile',
'GenerateForm',
'questionDefinitionForm'
'questionDefinitionForm',
'CreateSelect2'
];

View File

@ -1,193 +0,0 @@
/**
* Takes a finalized question and displays it on the survey maker page
*
* FinalizeQuestion({
* scope: $scope containing list of survey form fields
* question: question object that was submitted by the question form
* id: id of job template that survey is attached to
* callback: $scope.$emit label to call when delete is completed
* })
*
*/
export default
function FinalizeQuestion(GetBasePath, Rest, Wait, ProcessErrors, $compile, Empty, $filter, questionScope) {
return function(params) {
var question = params.question,
scope = questionScope(question, params.scope),
index = params.index,
required,
element,
max,
min,
defaultValue,
html = "";
question.index = index;
question.question_name = $filter('sanitize')(question.question_name);
question.question_description = (question.question_description) ? $filter('sanitize')(question.question_description) : undefined;
if(!$('#question_'+question.index+':eq(0)').is('div')){
html+='<div id="question_'+question.index+'" class="question_final row"></div>';
$('#finalized_questions').append(html);
}
required = (question.required===true) ? "prepend-asterisk" : "";
html = '<div class="question_title col-xs-12">';
html += '<label for="'+question.variable+'"><span class="label-text '+required+'"> '+question.question_name+'</span></label>';
html += '</div>';
if(!Empty(question.question_description)){
html += '<div class="col-xs-12 description"><i>'+question.question_description+'</i></div>\n';
}
if(question.type === 'text' ){
defaultValue = (question.default) ? question.default : "";
defaultValue = $filter('sanitize')(defaultValue);
defaultValue = scope.serialize(defaultValue);
html+='<div class="row">'+
'<div class="col-xs-8">'+
'<input type="text" placeholder="'+defaultValue+'" class="form-control ng-pristine ng-invalid-required ng-invalid final" required="" readonly>'+
'</div></div>';
}
if(question.type === "textarea"){
defaultValue = (question.default) ? question.default : (question.default_textarea) ? question.default_textarea: "" ;
defaultValue = $filter('sanitize')(defaultValue);
defaultValue = scope.serialize(defaultValue);
html+='<div class="row">'+
'<div class="col-xs-8 input_area">'+
'<textarea class="form-control ng-pristine ng-invalid-required ng-invalid final" required="" rows="3" readonly>'+defaultValue+'</textarea>'+
'</div></div>';
}
if(question.type === 'multiplechoice' || question.type === "multiselect"){
question.default = question.default_multiselect || question.default;
var defaultScopePropertyName =
question.variable + '_default';
if (question.default) {
if (question.type === 'multiselect' && typeof question.default.split === 'function') {
scope[defaultScopePropertyName] = question.default.split('\n');
} else if (question.type !== 'multiselect') {
scope[defaultScopePropertyName] = question.default;
}
} else {
scope[defaultScopePropertyName] = '';
}
html += '<div class="row">';
html += '<div class="col-xs-8">';
html += '<div class="SurveyControls-selectWrapper">';
html += '<survey-question type="' + question.type + '" question="question" ng-required="' + question.required + '" ng-model="' + defaultScopePropertyName + '" ng-disabled=true></survey-question>';
html += '</div>';
html += '</div>';
html += '</div>';
}
if(question.type === 'password'){
defaultValue = (question.default) ? question.default : "";
defaultValue = $filter('sanitize')(defaultValue);
defaultValue = scope.serialize(defaultValue);
html+='<div class="row">'+
' <div class="col-xs-8 input_area input-group">'+
'<span class="input-group-btn">'+
'<button class="btn btn-default survey-maker-password show_input_button" id="'+question.variable+'_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="toggleInput(&quot;#'+question.variable+'&quot;)" data-original-title="" title="">ABC</button>'+
'</span>'+
'<input id="'+ question.variable +'" type="password" ng-model="default_password" name="'+ question.variable +'" class="form-control ng-pristine ng-valid-api-error ng-invalid" autocomplete="false" readonly>'+
'</div>'+
'</div>';
}
if(question.type === 'integer'){
min = (!Empty(question.min)) ? question.min : "";
max = (!Empty(question.max)) ? question.max : "" ;
defaultValue = (!Empty(question.default)) ? question.default : (!Empty(question.default_int)) ? question.default_int : "" ;
html+='<div class="row">'+
'<div class="col-xs-8 input_area">'+
'<input type="number" class="final form-control" name="'+question.variable+'" min="'+min+'" max="'+max+'" value="'+defaultValue+'" readonly>'+
'</div></div>';
}
if(question.type === "float"){
min = (!Empty(question.min)) ? question.min : "";
max = (!Empty(question.max)) ? question.max : "" ;
defaultValue = (!Empty(question.default)) ? question.default : (!Empty(question.default_float)) ? question.default_float : "" ;
html+='<div class="row">'+
'<div class="col-xs-8 input_area">'+
'<input type="number" class="final form-control" name="'+question.variable+'" min="'+min+'" max="'+max+'" value="'+defaultValue+'" readonly>'+
'</div></div>';
}
html += '<div class="col-xs-12 text-right question_actions">';
html += '<a id="edit-question_'+question.index+'" data-placement="top" aw-tool-tip="Edit question" data-original-title="" title=""><i class="fa fa-pencil"></i> </a>';
html += '<a id="delete-question_'+question.index+'" data-placement="top" aw-tool-tip="Delete question" data-original-title="" title=""><i class="fa fa-trash-o"></i> </a>';
html += '<a id="question-up_'+question.index+'" data-placement="top" aw-tool-tip="Move up" data-original-title="" title=""><i class="fa fa-arrow-up"></i> </a>';
html += '<a id="question-down_'+question.index+'" data-placement="top" aw-tool-tip="Move down" data-original-title="" title=""><i class="fa fa-arrow-down"></i> </a>';
html+='</div></div>';
$('#question_'+question.index).append(html);
element = angular.element(document.getElementById('question_'+question.index));
// // element.html(html);
//element.css('opacity', 0.7);
$compile(element)(scope);
$('#add_question_btn').show();
$('#add_question_btn').removeAttr('disabled');
$('#add_question_btn').focus();
$('#survey_maker_save_btn').removeAttr('disabled');
// Sometimes the $event.target returns the anchor element that wraps the icon, and sometimes the icon itself
// is returned. So for each icon click event we check to see which target the user clicked, and depending no which one
// they clicked, we move up the dom hierarchy to get the index on the question. Ultimatley the object that is passed to
// each one of these functions should be the index of the question that the user is trying to perform an action on.
$('#delete-question_'+question.index+'').on('click', function($event){
if($event.target.nodeName==="A"){
scope.deleteQuestion($event.target.parentElement.parentElement.id.split('_')[1]);
}
else if($event.target.nodeName === "I"){
scope.deleteQuestion($event.target.parentElement.parentElement.parentElement.id.split('_')[1]);
}
});
$('#edit-question_'+question.index+'').on('click', function($event){
if($event.target.nodeName==="A"){
scope.editQuestion($event.target.parentElement.parentElement.id.split('_')[1]);
}
else if($event.target.nodeName === "I"){
scope.editQuestion($event.target.parentElement.parentElement.parentElement.id.split('_')[1]);
}
});
$('#question-up_'+question.index+'').on('click', function($event){
if($event.target.nodeName==="A"){
scope.questionUp($event.target.parentElement.parentElement.id.split('_')[1]);
}
else if($event.target.nodeName === "I"){
scope.questionUp($event.target.parentElement.parentElement.parentElement.id.split('_')[1]);
}
});
$('#question-down_'+question.index+'').on('click', function($event){
if($event.target.nodeName==="A"){
scope.questionDown($event.target.parentElement.parentElement.id.split('_')[1]);
}
else if($event.target.nodeName === "I"){
scope.questionDown($event.target.parentElement.parentElement.parentElement.id.split('_')[1]);
}
});
};
}
FinalizeQuestion.$inject =
[ 'GetBasePath',
'Rest',
'Wait',
'ProcessErrors',
'$compile',
'Empty',
'$filter',
'questionScope'
];

View File

@ -1,9 +1,7 @@
import questionScope from './question-scope.factory';
import finalize from './finalize.factory';
import edit from './edit.factory';
export default
angular.module('jobTemplates.surveyMaker.questions', [])
.factory('finalizeQuestion', finalize)
.factory('questionScope', questionScope)
.factory('editQuestion', edit);

View File

@ -1,43 +1,31 @@
/* jshint unused: vars */
import {templateUrl} from '../../../shared/template-url/template-url.factory';
function link($timeout, scope, element, attrs, ngModel) {
attrs.width = attrs.width || '100%';
function link($timeout, CreateSelect2, scope, element, attrs, ngModel) {
$timeout(function() {
$.fn.select2.amd.require(
[ 'select2/utils',
'select2/dropdown',
'select2/dropdown/search',
'select2/dropdown/attachContainer',
'select2/dropdown/closeOnSelect',
'select2/dropdown/minimumResultsForSearch'
],
function(Utils, Dropdown, Search, AttachContainer, CloseOnSelect, MinimumResultsForSearch) {
var CustomAdapter =
_.reduce([Search, AttachContainer, CloseOnSelect, MinimumResultsForSearch],
function(Adapter, Decorator) {
return Utils.Decorate(Adapter, Decorator);
}, Dropdown);
element.find('select').select2(
{ multiple: scope.isMultipleSelect(),
minimumResultsForSearch: Infinity,
theme: 'bootstrap',
width: attrs.width,
dropdownAdapter: CustomAdapter
});
});
// select2-ify the dropdown. If the preview flag is passed here
// and it's true then we don't want to use a custom dropdown adapter.
// The reason for this is that the custom dropdown adapter breaks
// the draggability of this element. We're able to get away with this
// in preview mode (survey create/edit) because the element is disabled
// and we don't actually need the dropdown portion. Note that the custom
// dropdown adapter is used to get the dropdown contents to show up in
// a modal.
CreateSelect2({
element: element.find('select'),
multiple: scope.isMultipleSelect(),
customDropdownAdapter: scope.preview ? false : true
});
});
}
export default
[ '$timeout',
function($timeout) {
[ '$timeout', 'CreateSelect2',
function($timeout, CreateSelect2) {
var directive =
{ restrict: 'E',
require: 'ngModel',
@ -47,10 +35,11 @@ export default
question: '=',
isRequired: '=ngRequired',
selectedValue: '=ngModel',
isDisabled: '=ngDisabled'
isDisabled: '=ngDisabled',
preview: '='
},
templateUrl: templateUrl('job-templates/survey-maker/render/multiple-choice'),
link: _.partial(link, $timeout)
link: _.partial(link, $timeout, CreateSelect2)
};
return directive;
}

View File

@ -1,5 +1,5 @@
<div>
<select class="form-control" ng-model="selectedValue" multi-select ng-required="isRequired" ng-disabled="isDisabled">
<select class="form-control SurveyMaker-previewSelect" ng-model="selectedValue" multi-select ng-required="isRequired" ng-disabled="isDisabled">
<option ng-repeat="choice in choices" value="{{choice}}" ng-selected="selectedValue.indexOf(choice) !== -1">{{choice}}</option>
</select>
</div>

View File

@ -23,31 +23,104 @@ function findQuestionByIndex(questions, index) {
});
}
function link(scope, element, attrs) {
function link($sce, $filter, Empty, scope, element, attrs) {
function serialize(expression) {
return $sce.getTrustedHtml(expression);
}
function sanitizeDefault() {
var defaultValue = "";
if(scope.question.type === 'text'|| scope.question.type === "password" ){
defaultValue = (scope.question.default) ? scope.question.default : "";
defaultValue = $filter('sanitize')(defaultValue);
defaultValue = serialize(defaultValue);
}
if(scope.question.type === "textarea"){
defaultValue = (scope.question.default) ? scope.question.default : (scope.question.default_textarea) ? scope.question.default_textarea: "" ;
defaultValue = $filter('sanitize')(defaultValue);
defaultValue = serialize(defaultValue);
}
if(scope.question.type === 'multiplechoice' || scope.question.type === "multiselect"){
scope.question.default = scope.question.default_multiselect || scope.question.default;
if (scope.question.default) {
if (scope.question.type === 'multiselect' && typeof scope.question.default.split === 'function') {
defaultValue = scope.question.default.split('\n');
} else if (scope.question.type !== 'multiselect') {
defaultValue = scope.question.default;
}
} else {
defaultValue = '';
}
}
if(scope.question.type === 'integer'){
var min = (!Empty(scope.question.min)) ? scope.question.min : "";
var max = (!Empty(scope.question.max)) ? scope.question.max : "" ;
defaultValue = (!Empty(scope.question.default)) ? scope.question.default : (!Empty(scope.question.default_int)) ? scope.question.default_int : "" ;
}
if(scope.question.type === "float"){
var min = (!Empty(scope.question.min)) ? scope.question.min : "";
var max = (!Empty(scope.question.max)) ? scope.question.max : "" ;
defaultValue = (!Empty(scope.question.default)) ? scope.question.default : (!Empty(scope.question.default_float)) ? scope.question.default_float : "" ;
}
scope.defaultValue = defaultValue;
}
//for toggling the input on password inputs
scope.toggleInput = function(id) {
var buttonId = id + "_show_input_button",
inputId = id,
buttonInnerHTML = $(buttonId).html();
if (buttonInnerHTML.indexOf("SHOW") > -1) {
$(buttonId).html("HIDE");
$(inputId).attr("type", "text");
} else {
$(buttonId).html("SHOW");
$(inputId).attr("type", "password");
}
};
if (!scope.question) {
scope.question = findQuestionByIndex(scope.surveyQuestions, Number(attrs.index));
}
// Split out choices to be consumed by the multiple-choice directive
if (!_.isUndefined(scope.question.choices)) {
scope.choices = scope.question.choices.split('\n');
}
sanitizeDefault();
}
export default
function() {
var directive =
{ restrict: 'E',
scope:
{ question: '=',
selectedValue: '=ngModel',
surveyQuestions: '=',
isRequired: '@ngRequired',
isDisabled: '@ngDisabled'
},
templateUrl: templateUrl('job-templates/survey-maker/render/survey-question'),
link: link
};
[
'$sce', '$filter', 'Empty',
function($sce, $filter, Empty) {
var directive =
{ restrict: 'E',
scope:
{ question: '=',
surveyQuestions: '=',
isRequired: '@ngRequired',
isDisabled: '@ngDisabled',
preview: '='
},
templateUrl: templateUrl('job-templates/survey-maker/render/survey-question'),
link: _.partial(link, $sce, $filter, Empty)
};
return directive;
}
return directive;
}
];

View File

@ -1,8 +1,30 @@
<multiple-choice
multi-select="question.type === 'multiselect'"
question="question"
choices="choices"
ng-required="isRequired === 'true'"
ng-model="selectedValue"
ng-disabled="isDisabled === 'true'">
</multiple-choice>
<div ng-if="question.type === 'text'">
<input type="text" ng-model="defaultValue" class="form-control ng-pristine ng-invalid-required ng-invalid final" required="" readonly>
</div>
<div ng-if="question.type === 'textarea'" class="input_area">
<textarea class="form-control ng-pristine ng-invalid-required ng-invalid final" required="" rows="3" readonly>{{defaultValue}}</textarea>
</div>
<div ng-if="question.type === 'multiplechoice' || question.type === 'multiselect'" class="SurveyMaker-previewInput">
<multiple-choice
multi-select="question.type === 'multiselect'"
question="question"
choices="choices"
ng-required="isRequired === 'true'"
ng-model="defaultValue"
ng-disabled="isDisabled === 'true'"
preview="preview">
</multiple-choice>
</div>
<div ng-if="question.type === 'password'" class="input_area input-group">
<span class="input-group-btn">
<button class="btn btn-default SurveyMaker-previewPasswordButton" id="{{ question.variable + '_show_input_button' }}" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="toggleInput('#' + question.variable)" data-original-title="" title="">SHOW</button>
</span>
<input id="{{question.variable}}" type="password" name="" class="form-control ng-pristine ng-valid-api-error ng-invalid" autocomplete="false" ng-model="defaultValue" readonly>
</div>
<div ng-if="question.type === 'integer'" class="input_area">
<input type="number" class="final form-control" name="" min="question.min" max="question.max" ng-value="defaultValue" readonly>
</div>
<div ng-if="question.type === 'float'" class="input_area">
<input type="number" class="final form-control" name="" min="question.min" max="question.max" ng-value="defaultValue" readonly>
</div>

View File

@ -15,10 +15,11 @@ export default
addTitle: 'Add Question',
editTitle: 'Edit Question',
titleClass: 'Form-secondaryTitle',
base: 'survey_question',
name: 'survey_question',
well: true,
twoColumns: true,
cancelButton: false,
fields: {
question_name: {
@ -28,7 +29,8 @@ export default
addRequired: true,
editRequired: true,
column: 1,
awSurveyQuestion: true
awSurveyQuestion: true,
class: 'Form-formGroup--singleColumn'
},
question_description: {
realName: 'question_description',
@ -36,16 +38,17 @@ export default
type: 'text',
addRequired: false,
editRequired: false,
column: 1
column: 1,
class: 'Form-formGroup--singleColumn'
},
variable: {
ealName: 'variable',
type: 'custom',
control:'<label for="variable"><span class="label-text prepend-asterisk"> Answer Variable Name</span>'+
control:'<label for="variable"><span class="Form-inputLabel prepend-asterisk"> ANSWER VARIABLE NAME</span>'+
'<a id="awp-variable" href="" aw-pop-over="<p>The suggested format for variable names is lowercase and underscore-separated. Also note that this field cannot accept variable names with spaces.</p><p>For example: <br>foo_bar<br>'+
'user_id<br>host_name<br><div class=&quot;popover-footer&quot;><span class=&quot;key&quot;>esc</span> or click to close</div>" '+
'data-placement="right" data-container="body" data-title="Answer Variable Name" class="help-link" data-original-title="" title="" tabindex="-1"><i class="fa fa-question-circle"></i></a> </label>'+
'<div><input type="text" ng-model="variable" name="variable" id="survey_question_variable" class="form-control ng-pristine ng-invalid ng-invalid-required" required="" aw-survey-variable-name>'+
'<div><input type="text" ng-model="variable" name="variable" id="survey_question_variable" class="form-control Form-textInput ng-pristine ng-invalid ng-invalid-required" required="" aw-survey-variable-name>'+
'<div class="error ng-hide" id="survey_question-variable-required-error" ng-show="survey_question_form.variable.$dirty && survey_question_form.variable.$error.required">Please enter an answer variable name.</div>'+
'<div class="error ng-hide" id="survey_question-variable-variable-error" ng-show="survey_question_form.variable.$dirty && survey_question_form.variable.$error.variable">Please remove the illegal character from the survey question variable name.</div>'+
'<div class="error ng-hide" id=survey_question-variable-duplicate-error" ng-show="duplicate">This question variable is already in use. Please enter a different variable name.</div>' +
@ -53,7 +56,8 @@ export default
'</div>',
addRequired: true,
editRequired: true,
column: 1
column: 1,
class: 'Form-formGroup--singleColumn'
},
type: {
realName: 'answer_type',
@ -64,7 +68,8 @@ export default
addRequired: true,
editRequired: true,
column: 2,
ngChange: 'typeChange()'
ngChange: 'typeChange()',
class: 'Form-formGroup--singleColumn'
},
choices: {
realName: 'answer_options',
@ -81,20 +86,21 @@ export default
dataTitle: 'Multiple Choice Options',
dataPlacement: 'right',
dataContainer: "body",
column: 2
column: 2,
class: 'Form-formGroup--singleColumn'
},
text_options: {
realName: 'answer_options',
type: 'custom',
control:'<div class="row">'+
'<div class="col-xs-6">'+
'<label for="text_min"><span class="label-text">Minimum Length</span></label><input id="text_min" type="number" name="text_min" ng-model="text_min" min=0 aw-min="0" aw-max="text_max" class="form-control" integer />'+
'<label for="text_min"><span class="Form-inputLabel">Minimum Length</span></label><input id="text_min" name="text_min" ng-model="text_min" min=0 aw-min="0" aw-max="text_max" aw-spinner="text_min" integer>'+
'<div class="error" ng-show="survey_question_form.text_min.$error.integer || survey_question_form.text_min.$error.number">The minimum length you entered is not a valid number. Please enter a whole number.</div>'+
'<div class="error" ng-show="survey_question_form.text_min.$error.awMax">The minimium length is too high. Please enter a lower number.</div>'+
'<div class="error" ng-show="survey_question_form.text_min.$error.awMin">The minimum length is too low. Please enter a positive number.</div>'+
'</div>'+
'<div class="col-xs-6">'+
'<label for="text_max"><span class="label-text">Maximum Length</span></label><input id="text_max" type="number" name="text_max" ng-model="text_max" aw-min="text_min || 0" min=0 class="form-control" integer >'+
'<label for="text_max"><span class="Form-inputLabel">Maximum Length</span></label><input id="text_max" name="text_max" ng-model="text_max" aw-min="text_min || 0" min=0 aw-spinner="text_max" integer>'+
'<div class="error" ng-show="survey_question_form.text_max.$error.integer || survey_question_form.text_max.$error.number">The maximum length you entered is not a valid number. Please enter a whole nnumber.</div>'+
'<div class="error" ng-show="survey_question_form.text_max.$error.awMin">The maximum length is too low. Please enter a number larger than the minimum length you set.</div>'+
'</div>'+
@ -102,20 +108,21 @@ export default
ngShow: 'type.type==="text" ',
addRequired: true,
editRequired: true,
column: 2
column: 2,
class: 'Form-formGroup--singleColumn'
},
textarea_options: {
realName: 'answer_options',
type: 'custom',
control:'<div class="row">'+
'<div class="col-xs-6">'+
'<label for="textarea_min"><span class="label-text">Minimum Length</span></label><input id="textarea_min" type="number" name="textarea_min" ng-model="textarea_min" min=0 aw-min="0" aw-max="textarea_max" class="form-control" integer />'+
'<label for="textarea_min"><span class="Form-inputLabel">Minimum Length</span></label><input id="textarea_min" type="number" name="textarea_min" ng-model="textarea_min" min=0 aw-min="0" aw-max="textarea_max" class="form-control Form-textInput" integer />'+
'<div class="error" ng-show="survey_question_form.textarea_min.$error.integer || survey_question_form.textarea_min.$error.number">The minimum length you entered is not a valid number. Please enter a whole number.</div>'+
'<div class="error" ng-show="survey_question_form.textarea_min.$error.awMax">The minimium length is too high. Please enter a lower number.</div>'+
'<div class="error" ng-show="survey_question_form.textarea_min.$error.awMin">The minimum length is too low. Please enter a positive number.</div>'+
'</div>'+
'<div class="col-xs-6">'+
'<label for="textarea_max"><span class="label-text">Maximum Length</span></label><input id="textarea_max" type="number" name="textarea_max" ng-model="textarea_max" aw-min="textarea_min || 0" min=0 class="form-control" integer >'+
'<label for="textarea_max"><span class="Form-inputLabel">Maximum Length</span></label><input id="textarea_max" type="number" name="textarea_max" ng-model="textarea_max" aw-min="textarea_min || 0" min=0 class="form-control Form-textInput" integer >'+
'<div class="error" ng-show="survey_question_form.textarea_max.$error.integer || survey_question_form.textarea_max.$error.number">The maximum length you entered is not a valid number. Please enter a whole number.</div>'+
'<div class="error" ng-show="survey_question_form.textarea_max.$error.awMin">The maximum length is too low. Please enter a number larger than the minimum length you set.</div>'+
'</div>'+
@ -123,20 +130,21 @@ export default
ngShow: 'type.type==="textarea" ',
addRequired: true,
editRequired: true,
column: 2
column: 2,
class: 'Form-formGroup--singleColumn'
},
password_options: {
realName: 'answer_options',
type: 'custom',
control:'<div class="row">'+
'<div class="col-xs-6">'+
'<label for="password_min"><span class="label-text">Minimum Length</span></label><input id="password_min" type="number" name="password_min" ng-model="password_min" min=0 aw-min="0" aw-max="password_max" class="form-control" integer />'+
'<label for="password_min"><span class="Form-inputLabel">Minimum Length</span></label><input id="password_min" type="number" name="password_min" ng-model="password_min" min=0 aw-min="0" aw-max="password_max" class="form-control Form-textInput" integer />'+
'<div class="error" ng-show="survey_question_form.password_min.$error.integer || survey_question_form.password_min.$error.number">The minimum length you entered is not a valid number. Please enter a whole number.</div>'+
'<div class="error" ng-show="survey_question_form.password_min.$error.awMax">The minimium length is too high. Please enter a lower number.</div>'+
'<div class="error" ng-show="survey_question_form.password_min.$error.awMin">The minimum length is too low. Please enter a positive number.</div>'+
'</div>'+
'<div class="col-xs-6">'+
'<label for="password_max"><span class="label-text">Maximum Length</span></label><input id="password_max" type="number" name="password_max" ng-model="password_max" aw-min="password_min || 0" min=0 class="form-control" integer >'+
'<label for="password_max"><span class="Form-inputLabel">Maximum Length</span></label><input id="password_max" type="number" name="password_max" ng-model="password_max" aw-min="password_min || 0" min=0 class="form-control Form-textInput" integer >'+
'<div class="error" ng-show="survey_question_form.password_max.$error.integer || survey_question_form.password_max.$error.number">The maximum length you entered is not a valid number. Please enter a whole number.</div>'+
'<div class="error" ng-show="survey_question_form.password_max.$error.awMin">The maximum length is too low. Please enter a number larger than the minimum length you set.</div>'+
'</div>'+
@ -144,19 +152,20 @@ export default
ngShow: 'type.type==="password" ',
addRequired: true,
editRequired: true,
column: 2
column: 2,
class: 'Form-formGroup--singleColumn'
},
int_options: {
realName: 'answer_options',
type: 'custom',
control:'<div class="row">'+
'<div class="col-xs-6">'+
'<label for="minimum"><span class="label-text">Minimum</span></label><input id="int_min" type="number" name="int_min" ng-model="int_min" aw-max="int_max" class="form-control" integer >'+
'<label for="minimum"><span class="Form-inputLabel">Minimum</span></label><input id="int_min" type="number" name="int_min" ng-model="int_min" aw-max="int_max" class="form-control Form-textInput" integer >'+
'<div class="error" ng-show="survey_question_form.int_min.$error.integer || survey_question_form.int_min.$error.number">Please enter a valid integer.</div>'+
'<div class="error" ng-show="survey_question_form.int_min.$error.awMax">Please enter a smaller integer.</div>'+
'</div>'+
'<div class="col-xs-6">'+
'<label for="minimum"><span class="label-text">Maximum</span></label><input id="int_max" type="number" name="int_max" ng-model="int_max" aw-min="int_min" class="form-control" integer >'+
'<label for="minimum"><span class="Form-inputLabel">Maximum</span></label><input id="int_max" type="number" name="int_max" ng-model="int_max" aw-min="int_min" class="form-control Form-textInput" integer >'+
'<div class="error" ng-show="survey_question_form.int_max.$error.integer || survey_question_form.int_max.$error.number">Please enter a valid integer.</div>'+
'<div class="error" ng-show="survey_question_form.int_max.$error.awMin">Please enter a larger integer.</div>'+
'</div>'+
@ -164,19 +173,20 @@ export default
ngShow: 'type.type==="integer" ',
addRequired: true,
editRequired: true,
column: 2
column: 2,
class: 'Form-formGroup--singleColumn'
},
float_options: {
realName: 'answer_options',
type: 'custom',
control: '<div class="row">'+
'<div class="col-xs-6">'+
'<label for="minimum"><span class="label-text">Minimum</span></label><input id="float_min" type="number" name="float_min" ng-model="float_min" class="form-control" smart-float aw-max="float_max">'+
'<label for="minimum"><span class="Form-inputLabel">Minimum</span></label><input id="float_min" type="number" name="float_min" ng-model="float_min" class="form-control Form-textInput" smart-float aw-max="float_max">'+
'<div class="error" ng-show="survey_question_form.float_min.$error.float || survey_question_form.float_min.$error.number">Please enter a valid float.</div>'+
'<div class="error" ng-show="survey_question_form.float_min.$error.awMax">Please enter a smaller float.</div>'+
'</div>'+
'<div class="col-xs-6">'+
'<label for="maximum"><span class="label-text">Maximum</span></label><input id="float_max" type="number" name="float_max" ng-model="float_max" class="form-control" smart-float aw-min="float_min">'+
'<label for="maximum"><span class="Form-inputLabel">Maximum</span></label><input id="float_max" type="number" name="float_max" ng-model="float_max" class="form-control Form-textInput" smart-float aw-min="float_min">'+
'<div class="error" ng-show="survey_question_form.float_max.$error.float">Please enter a valid float.</div>'+
'<div class="error" ng-show="survey_question_form.float_max.$error.awMin">Please enter a larger float.</div>'+
@ -185,15 +195,16 @@ export default
ngShow: 'type.type==="float" ',
addRequired: true,
editRequired: true,
column: 2
column: 2,
class: 'Form-formGroup--singleColumn'
},
default:{
realName: 'default_answer',
type: 'custom' ,
control: '<div class="form-group" >'+
'<label for="default"><span class="label-text">Default Answer</span></label>'+
'<label for="default"><span class="Form-inputLabel">Default Answer</span></label>'+
'<div>'+
'<input type="text" ng-model="default" name="default" id="default" class="form-control">'+
'<input type="text" ng-model="default" name="default" id="default" class="form-control Form-textInput">'+
'<div class="error ng-hide" id=survey_question-default-duplicate-error" ng-show="invalidChoice">Please enter an answer from the choices listed.</div>' +
'<div class="error ng-hide" id=survey_question-default-duplicate-error" ng-show="minTextError">The answer is shorter than the minimium length. Please make the answer longer. </div>' +
'<div class="error ng-hide" id=survey_question-default-duplicate-error" ng-show="maxTextError">The answer is longer than the maximum length. Please make the answer shorter. </div>' +
@ -201,72 +212,77 @@ export default
'</div>'+
'</div>',
column: 2,
ngShow: 'type.type === "text" || type.type === "multiplechoice" '
ngShow: 'type.type === "text" || type.type === "multiplechoice" ',
class: 'Form-formGroup--singleColumn'
},
default_multiselect: {
realName: 'default_answer' ,
type: 'custom',
control: '<div class="form-group">'+
'<label for="default_multiselect"><span class="label-text">Default Answer</span></label>'+
'<label for="default_multiselect"><span class="Form-inputLabel">Default Answer</span></label>'+
'<div>'+
'<textarea rows="3" ng-model="default_multiselect" name="default_multiselect" class="form-control ng-pristine ng-valid" id="default_multiselect" aw-watch=""></textarea>'+
'<textarea rows="3" ng-model="default_multiselect" name="default_multiselect" class="form-control Form-textArea ng-pristine ng-valid" id="default_multiselect" aw-watch=""></textarea>'+
'<div class="error ng-hide" id=survey_question-default_multiselect-duplicate-error" ng-show="invalidChoice">Please enter an answer/answers from the choices listed.</div>' +
'<div class="error api-error ng-binding" id="survey_question-default_multiselect-api-error" ng-bind="default_multiselect_api_error"></div>'+
'</div>'+
'</div>',
column: 2,
ngShow: 'type.type==="multiselect" '
ngShow: 'type.type==="multiselect" ',
class: 'Form-formGroup--singleColumn'
},
default_int: {
realName: 'default_answer',
type: 'custom',
control: '<div>'+
'<label for="default_int"><span class="label-text">Default Answer</span></label>'+
'<input type="number" ng-model="default_int" name="default_int" aw-min="int_min" aw-max="int_max" class="form-control" integer />'+
'<label for="default_int"><span class="Form-inputLabel">Default Answer</span></label>'+
'<input type="number" ng-model="default_int" name="default_int" aw-min="int_min" aw-max="int_max" class="form-control Form-textInput" integer />'+
'<div class="error" ng-show="survey_question_form.default_int.$error.number || survey_question_form.default_int.$error.integer">Please enter a valid integer.</div>'+
'<div class="error" ng-show="survey_question_form.default_int.$error.awMin || survey_question_form.default_int.$error.awMax"> Please enter a value in the range of {{int_min}} to {{int_max}}.</div>'+
'</div>',
column: 2,
ngShow: 'type.type === "integer" '
ngShow: 'type.type === "integer" ',
class: 'Form-formGroup--singleColumn'
},
default_float: {
realName: 'default_answer',
type: 'custom',
control: '<div>'+
'<label for="default_float"><span class="label-text">Default Answer</span></label>'+
'<input type="number" ng-model="default_float" name="default_float" aw-min="float_min" aw-max="float_max" class="form-control" />'+
'<label for="default_float"><span class="Form-inputLabel">Default Answer</span></label>'+
'<input type="number" ng-model="default_float" name="default_float" aw-min="float_min" aw-max="float_max" class="form-control Form-textInput" />'+
'<div class="error" ng-show="survey_question_form.default_float.$error.number || survey_question_form.default_float.$error.float">Please enter a valid float.</div>'+
'<div class="error" ng-show="survey_question_form.default_float.$error.awMin || survey_question_form.default_float.$error.awMax"> Please enter a value in the range of {{float_min}} to {{float_max}}!</div>'+
'</div>',
column: 2,
ngShow: 'type.type=== "float" '
ngShow: 'type.type=== "float" ',
class: 'Form-formGroup--singleColumn'
},
default_textarea: {
realName: "default_answer" ,
type: 'custom',
control: '<div class="form-group">'+
'<label for="default_textarea"><span class="label-text">Default Answer</span></label>'+
control: '<div class="form-group Form-formGroup Form-formGroup--singleColumn">'+
'<label for="default_textarea"><span class="Form-inputLabel">Default Answer</span></label>'+
'<div>'+
'<textarea rows="3" ng-model="default_textarea" name="default_textarea" class="form-control ng-valid ng-dirty" id="default_textarea"></textarea>'+
'<textarea rows="3" ng-model="default_textarea" name="default_textarea" class="form-control Form-textArea ng-valid ng-dirty" id="default_textarea"></textarea>'+
'<div class="error ng-hide" id=survey_question-default-duplicate-error" ng-show="minTextError">The answer is shorter than the minimium length. Please make the answer longer. </div>' +
'<div class="error ng-hide" id=survey_question-default-duplicate-error" ng-show="maxTextError">The answer is longer than the maximum length. Please make the answer shorter. </div>' +
'<div class="error api-error ng-binding" id="survey_question-default_textarea-api-error" ng-bind="default_textarea_api_error"></div>'+
'</div>'+
'</div>',
column : 2,
ngShow: 'type.type === "textarea" '
column : 2,
ngShow: 'type.type === "textarea" ',
class: 'Form-formGroup--singleColumn'
},
default_password: {
realName: 'default_answer' ,
type: 'custom' ,
control: '<div class="form-group">'+
'<label for="default_password"><span class="label-text">Default Answer</span></label>'+
'<label for="default_password"><span class="Form-inputLabel">Default Answer</span></label>'+
'<div>'+
'<div class="input-group">'+
'<span class="input-group-btn">'+
'<button class="btn btn-default show_input_button" id="default_password_show_input_button" aw-tool-tip="Toggle the display of plaintext." aw-tip-placement="top" ng-click="toggleInput(&quot;#default_password&quot;)" data-original-title="" title="">Show</button>'+
'</span>'+
'<input id="default_password" type="password" ng-model="default_password" name="default_password" class="form-control ng-pristine ng-valid-api-error ng-invalid" autocomplete="false">'+
'<input id="default_password" type="password" ng-model="default_password" name="default_password" class="form-control Form-textInput ng-pristine ng-valid-api-error ng-invalid" autocomplete="false">'+
'</div>'+
'<div class="error ng-hide" id=survey_question-default-duplicate-error" ng-show="minTextError">The answer is shorter than the minimium length. Please make the answer longer. </div>' +
'<div class="error ng-hide" id=survey_question-default-password-duplicate-error" ng-show="maxTextError">The answer is longer than the maximum length. Please make the answer shorter. </div>' +
@ -274,7 +290,8 @@ export default
'</div>'+
'</div>',
column: 2,
ngShow: 'type.type === "password" '
ngShow: 'type.type === "password" ',
class: 'Form-formGroup--singleColumn'
},
required: {
realName: 'required_answer',
@ -282,20 +299,22 @@ export default
type: 'checkbox',
addRequired: false,
editRequired: false,
column: 2
column: 2,
class: 'Form-formGroup--singleColumn'
}
},
buttons: {
question_cancel : {
label: 'Cancel',
'class' : 'btn btn-default',
ngClick: 'cancelQuestion($event)'
},
submit_question: {
ngClick: 'submitQuestion($event)',
ngDisabled: true,
'class': 'btn btn-sm btn-primary',
label: 'Add Question'
'class': 'btn btn-sm Form-saveButton',
label: '{{editQuestionIndex === null ? "ADD" : "UPDATE"}}'
},
question_cancel : {
label: 'Cancel',
'class' : 'btn btn-default Form-cancelButton',
ngClick: 'generateAddQuestionForm()',
ngDisabled: 'survey_question_form.$pristine'
}
}

View File

@ -0,0 +1,212 @@
@import "awx/ui/client/src/shared/branding/colors.default.less";
.SurveyMaker-dialog {
padding: 0px;
.ui-dialog-buttonpane, .ui-dialog-titlebar {
display:none;
}
}
.SurveyMaker-header {
display: flex;
align-items: center;
}
.SurveyMaker-title {
align-items: center;
flex: 1 0 auto;
display: flex;
}
.SurveyMaker-titleText {
color: @list-title-txt;
font-size: 14px;
font-weight: bold;
margin-right: 10px;
text-transform: uppercase;
}
.SurveyMaker-titleLockup {
margin-left: 4px;
margin-right: 6px;
display: inline-block;
margin-top: 0px;
padding-bottom: 2px;
vertical-align: bottom;
}
.SurveyMaker-titleLockup:before {
content: "\007C";
color: @default-icon-hov;
display: block;
font-size: 13px;
}
.SurveyMaker-close {
justify-content: flex-end;
display: flex;
}
.SurveyMaker-exit{
cursor:pointer;
padding:0px;
border: none;
height:20px;
font-size: 20px;
background-color:@default-bg;
color:@default-second-border;
transition: color 0.2s;
line-height:1;
}
.SurveyMaker-exit:hover{
color:@default-icon;
}
.SurveyMaker-content {
display: flex;
margin-top: 25px;
}
.SurveyMaker-questionPanel {
display: flex;
flex: 0 0 475px;
}
.SurveyMaker-previewPanel {
display: flex;
flex: 0 0 637px;
}
.SurveyMaker-separatorPanel {
display: flex;
flex: 0 0 51px;
}
.SurveyMaker-contentSeparator {
width: 1px;
background-color: @default-list-header-bg;
margin: 0px 25px;
}
.SurveyMaker-panelHeader {
color: @default-icon;
padding-bottom: 20px;
min-height: 40px;
flex: 0 0 auto;
text-transform: uppercase;
font-size: 14px;
font-weight: bold;
white-space: nowrap;
}
.SurveyMaker-panelBody {
flex: 1 0 auto;
padding-bottom: 20px;
}
.SurveyMaker-panelFooter {
flex: 0 0 auto;
}
.SurveyMaker-noQuestions {
color: @default-icon;
}
.SurveyMaker-deleteButton {
font-size: 16px;
height: 30px;
min-width: 30px;
color: @list-action-icon;
background-color: @list-actn-bg;
border: none;
border-radius: 50%;
margin-right: 15px;
}
.SurveyMaker-deleteButton:hover {
background-color: @list-actn-del-bg-hov !important;
color: @list-actn-icn-hov;
}
.SurveyMaker-previewLabel {
text-transform: uppercase;
color: @default-interface-txt;
font-weight: normal;
font-size: small;
width: 100%;
}
.SurveyMaker-deleteOverlay {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background: rgba(0,0,0,0.3);
z-index: 3;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
.SurveyMaker-deleteModal {
height: 200px;
width: 600px;
background-color: @default-bg;
border-radius: 5px;
}
.SurveyMaker-previewInputRow {
display: flex;
margin-bottom: 20px;
}
.SurveyMaker-previewInput {
flex: 1 0 523px;
max-width: 523px;
}
.SurveyMaker-previewActions {
display: flex;
align-items: center;
margin-left: 20px;
}
.SurveyMaker-previewActions--selected {
background-color: @default-link !important;
color: @default-bg;
}
.SurveyMaker-previewRows {
position: relative;
min-height: 42px;
padding-left: 0px;
/* These classes are dynamically added via the angular-drag-and-drop-lists directive */
.dndPlaceholder {
display: block;
background-color: @default-tertiary-bg;
padding: 10px 15px;
min-height: 42px;
margin-bottom: 20px;
border-radius: 4px;
color: @default-interface-txt;
}
.dndDraggingSource {
display: none;
}
}
.SurveyMaker-previewRow {
position: relative;
display: block;
/* Disable text selection if item is not draggable */
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.SurveyMaker-reorderButton {
color: @default-icon;
margin-right: 10px;
display: flex;
align-items: center;
cursor: -moz-grab;
cursor: -webkit-grab;
cursor: grab;
}
.SurveyMaker-reorderButton:hover {
color: @default-interface-txt;
}
.SurveyMaker-previewPasswordButton {
padding: 7px 15px!important;
}
.SurveyMaker-previewInput {
.select2-container--disabled {
opacity: inherit!important;
}
}
.SurveyMaker-previewMultiSelect {
background-color: #EEEEEE!important;
cursor: not-allowed!important;
}

View File

@ -3,17 +3,19 @@ export default
return function(params) {
var scope = params.scope;
// This variable controls the survey on/off toggle beside the create survey
// modal title. We want this toggle to be on by default
scope.survey_enabled = true;
if (scope.removeDialogReady) {
scope.removeDialogReady();
}
scope.removeDialogReady = scope.$on('DialogReady', function() {
$('#survey-modal-dialog').dialog('open');
scope.addQuestion();
scope.generateAddQuestionForm();
});
Wait('start');
$('#form-container').empty();
scope.resetForm();
ShowSurveyModal({ title: "Add Survey", scope: scope, callback: 'DialogReady' });
ShowSurveyModal({ scope: scope, callback: 'DialogReady' });
};
}

View File

@ -4,7 +4,6 @@
* DeleteSurvey({
* scope: $scope containing list of survey form fields
* id: id of job template that survey is attached to
* callback: $scope.$emit label to call when delete is completed
* })
*
*/
@ -14,7 +13,6 @@ export default
var scope = params.scope,
id = params.id,
// callback = params.callback,
url;
@ -25,11 +23,9 @@ export default
scope.survey_name = "";
scope.survey_description = "";
scope.survey_questions = [];
scope.closeSurvey('survey-modal-dialog');
Wait('stop');
scope.survey_exists = false;
$('#job_templates_delete_survey_btn').hide();
$('#job_templates_edit_survey_btn').hide();
$('#job_templates_create_survey_btn').show();
});

View File

@ -1,10 +1,8 @@
export default
function EditFactory($stateParams, SchedulerInit, ShowSurveyModal, Wait, Rest, ProcessErrors, GetBasePath, GenerateForm,
Empty, AddSurvey) {
function EditFactory(ShowSurveyModal, Wait, Rest, ProcessErrors, GetBasePath, Empty, AddSurvey) {
return function(params) {
var scope = params.scope,
id = params.id,
tempSurv = {},
url = GetBasePath('job_templates') + id + '/survey_spec/', i;
if (scope.removeDialogReady) {
@ -12,24 +10,13 @@ export default
}
scope.removeDialogReady = scope.$on('DialogReady', function() {
$('#survey-modal-dialog').dialog('open');
scope.generateAddQuestionForm();
});
scope.resetForm();
Wait('start');
//for adding a job template:
if(scope.mode === 'add'){
tempSurv.survey_name = scope.survey_name;
tempSurv.survey_description = scope.survey_description;
tempSurv.survey_questions = scope.survey_questions;
ShowSurveyModal({ title: "Edit Survey", scope: scope, callback: 'DialogReady' });
// scope.survey_name = tempSurv.survey_name;
// scope.survey_description = tempSurv.survey_description;
for(i=0; i<tempSurv.survey_questions.length; i++){
scope.finalizeQuestion(tempSurv.survey_questions[i], i);
}
}
//editing an existing job template:
else{
@ -39,14 +26,9 @@ export default
.success(function (data) {
if(!Empty(data)){
ShowSurveyModal({ title: "Edit Survey", scope: scope, callback: 'DialogReady' });
scope.survey_name = data.name;
scope.survey_description = data.description;
scope.survey_questions = data.spec;
for(i=0; i<scope.survey_questions.length; i++){
scope.finalizeQuestion(scope.survey_questions[i], i);
}
// scope.addQuestion();
Wait('stop');
} else {
AddSurvey({
@ -66,15 +48,11 @@ export default
EditFactory.$inject =
[ '$stateParams',
'SchedulerInit',
'showSurvey',
[ 'showSurvey',
'Wait',
'Rest',
'ProcessErrors',
'GetBasePath',
'GenerateForm',
'Empty',
'addSurvey'
];

View File

@ -1,6 +1,6 @@
export default
function Init($location, DeleteSurvey, EditSurvey, AddSurvey, GenerateForm, SurveyQuestionForm, Wait, Alert,
GetBasePath, Rest, ProcessErrors, $compile, FinalizeQuestion, EditQuestion, $sce) {
function Init(DeleteSurvey, EditSurvey, AddSurvey, GenerateForm, SurveyQuestionForm, Wait, Alert,
GetBasePath, Rest, ProcessErrors, $compile, EditQuestion, CreateSelect2) {
return function(params) {
var scope = params.scope,
id = params.id,
@ -20,230 +20,183 @@ export default
{name: 'Float', type: 'float'}
];
scope.serialize = function(expression){
return $sce.getTrustedHtml(expression);
};
scope.deleteSurvey = function() {
DeleteSurvey({
scope: scope,
id: id,
// callback: 'SchedulesRefresh'
});
};
scope.editSurvey = function() {
if(scope.mode==='add'){
for(i=0; i<scope.survey_questions.length; i++){
questions.push(scope.survey_questions[i]);
}
}
EditSurvey({
scope: scope,
id: id,
// callback: 'SchedulesRefresh'
});
};
/* SURVEY RELATED FUNCTIONS */
// Called when a job template does not have a saved survey. This simply sets some
// default variables and fills the add question form via form generator.
scope.addSurvey = function() {
AddSurvey({
scope: scope
});
};
scope.cancelSurvey = function(me){
if(scope.mode === 'add'){
questions = [];
// Called when a job template (new or existing) already has a "saved" survey
// In the case where a job template has not yet been created but a survey has
// been the data is just pulled out of the scope rather than from the server.
// (this is dictated by scope.mode)
scope.editSurvey = function() {
// Goes out and fetches the existing survey and populates the preview
EditSurvey({
scope: scope,
id: id
});
};
// This gets called after a user confirms survey deletion
scope.deleteSurvey = function() {
// Hide the delete overlay
scope.hideDeleteOverlay();
// Show the loading spinner
Wait('start');
// Call the delete survey factory which handles making the rest call
// and closing the modal after success
DeleteSurvey({
scope: scope,
id: id
});
};
// Called when the user hits cancel/close on the survey modal. This function
// goes out and cleans up the survey_questions on scope before destroying
// the modal.
scope.closeSurvey = function(id) {
if(scope.mode === 'add') {
// Clear out any "unsaved" survey questions
for (var i = scope.survey_questions.length - 1; i >= 0; i--) {
if (scope.survey_questions[i].new_question) {
scope.survey_questions.splice(i, 1);
}
}
}
else {
// Clear out the whole array, this data gets pulled in each time the modal is opened
scope.survey_questions = [];
}
$(me).dialog('close');
$('#' + id).dialog('destroy');
}
// Gets called when a user actually hits the save button. Functionality differs
// based on the mode. scope.mode="add" cleans up scope.survey_questions and
// destroys the modal, holding the survey questions in memory. scope.mode="edit"
// actually fires off the necessary server call(s) to add/update a survey.
scope.saveSurvey = function() {
Wait('start');
if(scope.mode ==="add"){
// Loop across the survey questions and remove any new_question flags
angular.forEach(scope.survey_questions, function(question, key) {
delete question['new_question'];
});
$('#survey-modal-dialog').dialog('destroy');
scope.survey_name = "";
scope.survey_description = "";
scope.$emit('SurveySaved');
}
else {
scope.survey_name = "";
scope.survey_description = "";
var updateSurveyQuestions = function() {
Rest.setUrl(GetBasePath('job_templates') + id + '/survey_spec/');
return Rest.post({name: scope.survey_name, description: scope.survey_description, spec: scope.survey_questions })
.success(function (data) {
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to add new survey. POST returned status: ' + status });
});
}
var updateSurveyEnabled = function() {
Rest.setUrl(GetBasePath('job_templates') + id+ '/');
return Rest.patch({"survey_enabled": scope.survey_enabled})
.success(function (data) {
})
.error(function (data, status) {
ProcessErrors(scope, data, status, form, {
hdr: 'Error!',
msg: 'Failed to retrieve save survey_enabled: ' + $routeParams.template_id + '. GET status: ' + status
});
});
}
updateSurveyQuestions()
.then(function() {
return updateSurveyEnabled();
})
.then(function() {
scope.closeSurvey('survey-modal-dialog');
scope.$emit('SurveySaved');
})
}
};
scope.addQuestion = function(){
// Gets called when the user clicks the on/off toggle beside the survey modal title.
scope.toggleSurveyEnabled = function() {
scope.survey_enabled = !scope.survey_enabled;
};
/* END SURVEY RELATED FUNCTIONS */
/* QUESTION RELATED FUNCTIONS */
// This injects the Add Question form into survey_maker_question_form
scope.generateAddQuestionForm = function(){
// This tmpMode logic is necessary because form generator seems to set scope.mode to match the mode that you pass it.
// So if a user is editing a job template (scope.mode='edit') but the JT doesn't have a survey then when we open the
// modal we need to make sure that scope.mode is still 'edit' after the Add Question form is injected.
// To avoid having to do this we'd need to track the job template mode in a variable other than scope.mode.
var tmpMode = scope.mode;
GenerateForm.inject(form, { id:'new_question', mode: 'add' , scope: scope, related: false});
GenerateForm.inject(form, { id:'survey_maker_question_form', mode: 'add' , scope: scope, related: false});
scope.mode = tmpMode;
scope.required = true; //set the required checkbox to true via the ngmodel attached to scope.required.
scope.text_min = null;
scope.text_max = null;
scope.int_min = null;
scope.int_max = null;
scope.float_min = null;
scope.float_max = null;
scope.duplicate = false;
scope.invalidChoice = false;
scope.minTextError = false;
scope.maxTextError = false;
scope.clearQuestion();
};
scope.addNewQuestion = function(){
// $('#add_question_btn').on("click" , function(){
scope.addQuestion();
$('#survey_question_question_name').focus();
$('#add_question_btn').attr('disabled', 'disabled');
$('#add_question_btn').hide();
$('#survey-save-button').attr('disabled' , 'disabled');
// });
};
// This gets called when a users clicks the pencil icon beside a question preview in order to edit it.
scope.editQuestion = function(index){
scope.duplicate = false;
// The edit question factory injects the edit form and fills the form with the question data from memory.
EditQuestion({
index: index,
scope: scope,
question: (scope.mode==='add') ? questions[index] : scope.survey_questions[index]
question: scope.survey_questions[index]
});
};
// Gets called when a user clicks the delete icon on a question in the survey preview
scope.showDeleteQuestion = function(deleteIndex) {
// Keep track of the question to be deleted on scope
scope.questionToBeDeleted = deleteIndex;
// Show the delete overlay with mode='question'
scope.showDeleteOverlay('question');
}
// Called after a user confirms question deletion (hitting the DELETE button on the delete question overlay).
scope.deleteQuestion = function(index){
element = $('.question_final:eq('+index+')');
element.remove();
if(scope.mode === 'add'){
questions.splice(index, 1);
scope.reorder();
if(questions.length<1){
$('#survey-save-button').attr('disabled', 'disabled');
}
}
else {
scope.survey_questions.splice(index, 1);
scope.reorder();
if(scope.survey_questions.length<1){
$('#survey-save-button').attr('disabled', 'disabled');
// Move the edit question index down by one if this question came before the
// one being edited in the array. This makes sure that our pointer to the question
// currently being edited gets updated independently from a deleted question.
if(GenerateForm.mode === 'edit' && !isNaN(scope.editQuestionIndex)){
if(scope.editQuestionIndex == index) {
// The user is deleting the question being edited - need to roll back to Add Question mode
scope.editQuestionIndex = null;
scope.generateAddQuestionForm();
}
else if(scope.editQuestionIndex > index) {
scope.editQuestionIndex--;
}
}
// Remove the question from the array
scope.survey_questions.splice(index, 1);
// Hide the delete overlay
scope.hideDeleteOverlay();
};
scope.cancelQuestion = function(event){
var elementID, key;
if(event.target.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.id==="new_question"){
$('#new_question .aw-form-well').remove();
$('#add_question_btn').show();
$('#add_question_btn').removeAttr('disabled');
if(scope.mode === 'add' && questions.length>0){
$('#survey-save-button').removeAttr('disabled');
}
if(scope.mode === 'edit' && scope.survey_questions.length>0 && scope.can_edit===true){
$('#survey-save-button').removeAttr('disabled');
}
} else {
elementID = event.target.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.id;
key = elementID.split('_')[1];
$('#'+elementID).empty();
if(scope.mode === 'add'){
if(questions.length>0){
$('#survey-save-button').removeAttr('disabled');
}
scope.finalizeQuestion(questions[key], Number(key));
}
else if(scope.mode=== 'edit' ){
if(scope.survey_questions.length>0 && scope.can_edit === true){
$('#survey-save-button').removeAttr('disabled');
}
scope.finalizeQuestion(scope.survey_questions[key] , Number(key));
}
}
};
scope.questionUp = function(index){
var animating = false,
clickedDiv = $('#question_'+index),
prevDiv = clickedDiv.prev(),
distance = clickedDiv.outerHeight();
if (animating) {
return;
}
if (prevDiv.length) {
animating = true;
$.when(clickedDiv.animate({
top: -distance
}, 600),
prevDiv.animate({
top: distance
}, 600)).done(function () {
prevDiv.css('top', '0px');
clickedDiv.css('top', '0px');
clickedDiv.insertBefore(prevDiv);
animating = false;
if ( scope.mode === 'add'){
i = questions[index];
questions[index] = questions[index-1];
questions[index-1] = i;
} else {
i = scope.survey_questions[index];
scope.survey_questions[index] = scope.survey_questions[index-1];
scope.survey_questions[index-1] = i;
}
scope.reorder();
});
}
};
scope.questionDown = function(index){
var clickedDiv = $('#question_'+index),
nextDiv = clickedDiv.next(),
distance = clickedDiv.outerHeight(),
animating = false;
if (animating) {
return;
}
if (nextDiv.length) {
animating = true;
$.when(clickedDiv.animate({
top: distance
}, 600),
nextDiv.animate({
top: -distance
}, 600)).done(function () {
nextDiv.css('top', '0px');
clickedDiv.css('top', '0px');
nextDiv.insertBefore(clickedDiv);
animating = false;
if(scope.mode === 'add'){
i = questions[index];
questions[index] = questions[Number(index)+1];
questions[Number(index)+1] = i;
} else {
i = scope.survey_questions[index];
scope.survey_questions[index] = scope.survey_questions[Number(index)+1];
scope.survey_questions[Number(index)+1] = i;
}
scope.reorder();
});
}
};
scope.reorder = function(){
if(scope.mode==='add'){
for(i=0; i<questions.length; i++){
questions[i].index=i;
$('.question_final:eq('+i+')').attr('id', 'question_'+i);
}
}
else {
for(i=0; i<scope.survey_questions.length; i++){
scope.survey_questions[i].index=i;
$('.question_final:eq('+i+')').attr('id', 'question_'+i);
}
}
};
scope.finalizeQuestion= function(data, index){
FinalizeQuestion({
scope: scope,
question: data,
id: id,
index: index
});
};
scope.typeChange = function() {
function clearTypeSpecificFields() {
scope.minTextError = false;
scope.maxTextError = false;
scope.default = "";
@ -263,6 +216,34 @@ export default
scope.int_max = "";
scope.float_min = "";
scope.float_max = "";
}
// Sets all of our scope variables used for adding/editing a question back to a clean state
scope.clearQuestion = function(){
clearTypeSpecificFields();
scope.editQuestionIndex = null;
scope.question_name = null;
scope.question_description = null;
scope.variable = null;
scope.required = true; //set the required checkbox to true via the ngmodel attached to scope.required.
scope.duplicate = false;
scope.invalidChoice = false;
scope.type = "";
// Make sure that the select2 dropdown for question type is clean
CreateSelect2({
element:'#survey_question_type',
multiple: false
});
// Set the whole form to pristine
scope.survey_question_form.$setPristine();
}
// Gets called when the "type" dropdown value changes. In that case, we want to clear out
// all the "type" specific fields/errors and start fresh.
scope.typeChange = function() {
clearTypeSpecificFields();
scope.survey_question_form.default.$setPristine();
scope.survey_question_form.default_multiselect.$setPristine();
scope.survey_question_form.default_float.$setPristine();
@ -274,6 +255,9 @@ export default
scope.survey_question_form.int_max.$setPristine();
};
// Function that gets called when a user hits ADD/UPDATE on the survey question form. This
// function handles some validation as well as eventually adding the question to the
// scope.survey_questions array.
scope.submitQuestion = function(event){
var data = {},
fld, i,
@ -340,36 +324,18 @@ export default
// validate that there aren't any questions using this var name.
if(GenerateForm.mode === 'add'){
if(scope.mode === 'add'){
for(fld in questions){
if(questions[fld].variable === scope.variable){
scope.duplicate = true;
}
}
}
else if (scope.mode === 'edit'){
for(fld in scope.survey_questions){
if(scope.survey_questions[fld].variable === scope.variable){
scope.duplicate = true;
}
for(fld in scope.survey_questions){
if(scope.survey_questions[fld].variable === scope.variable){
scope.duplicate = true;
}
}
}
if(GenerateForm.mode === 'edit'){
elementID = event.target.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.id;
key = elementID.split('_')[1];
if(scope.mode==='add'){
for(fld in questions){
if(questions[fld].variable === scope.variable && fld!==key){
scope.duplicate = true;
}
}
}
else if(scope.mode === 'edit'){
for(fld in scope.survey_questions){
if(scope.survey_questions[fld].variable === scope.variable && fld!==key){
scope.duplicate = true;
}
// Loop across the survey questions and see if a different question already has
// the same variable name
for(var i=0; i<scope.survey_questions.length; i++){
if(scope.survey_questions[i].variable === scope.variable && i!==scope.editQuestionIndex){
scope.duplicate = true;
}
}
@ -440,38 +406,22 @@ export default
}
Wait('stop');
if(scope.mode === 'add' || scope.mode==="edit" && scope.can_edit === true){
$('#survey-save-button').removeAttr('disabled');
}
if(GenerateForm.mode === 'add'){
if(scope.mode === 'add'){
questions.push(data);
$('#new_question .aw-form-well').remove();
$('#add_question_btn').show();
scope.finalizeQuestion(data , questions.length-1);
}
else if (scope.mode === 'edit'){
scope.survey_questions.push(data);
$('#new_question .aw-form-well').remove();
$('#add_question_btn').show();
scope.finalizeQuestion(data , scope.survey_questions.length-1);
}
// Flag this question as new
data.new_question = true;
scope.survey_questions.push(data);
$('#new_question .aw-form-well').remove();
$('#add_question_btn').show();
}
if(GenerateForm.mode === 'edit'){
elementID = event.target.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.id;
key = elementID.split('_')[1];
if(scope.mode==='add'){
questions[key] = data;
}
else if(scope.mode === 'edit'){
scope.survey_questions[key] = data;
}
$('#'+elementID).empty();
scope.finalizeQuestion(data , Number(key));
// Overwrite the survey question with the new data
scope.survey_questions[scope.editQuestionIndex] = data;
}
// Throw the user back to the "add question" form
scope.generateAddQuestionForm();
} catch (err) {
@ -479,71 +429,109 @@ export default
Alert("Error", "Error parsing extra variables. Parser returned: " + err);
}
};
scope.resetForm = function(){
html = '<div class="row">'+
'<div class="col-sm-12">'+
'<label for="survey"><span class="label-text prepend-asterisk"> Questions</span></label>'+
'<div id="survey_maker_question_area"></div>'+
'<div id="finalized_questions"></div>'+
'<button style="display:none" type="button" class="btn btn-sm btn-primary" id="add_question_btn" ng-click="addNewQuestion()" aw-tool-tip="Create a new question" data-placement="top" data-original-title="" title="" disabled><i class="fa fa-plus fa-lg"></i> New Question</button>'+
'<div id="new_question"></div>'+
'</div>'+
'</div>';
$('#survey-modal-dialog').html(html);
element = angular.element(document.getElementById('add_question_btn'));
$compile(element)(scope);
};
scope.saveSurvey = function() {
Wait('start');
if(scope.mode ==="add"){
$('#survey-modal-dialog').dialog('close');
if(questions.length>0){
scope.survey_questions = questions;
// This function is bound to the dnd-drop directive. When a question is dragged and
// dropped, this is the function that gets called.
scope.surveyQuestionDropped = function(dropIndex, question) {
// Handle moving the question to its new slot in scope.survey_questions
for(var i=0; i<scope.survey_questions.length; i++) {
if(angular.equals(scope.survey_questions[i], question)) {
// Check to make sure that the survey question was actually moved
if(i !== dropIndex) {
// Since the suvey question being "moved" is still technically in the array
// we need to adjust the drop index when moving a question down in the array
// See: https://github.com/marceljuenemann/angular-drag-and-drop-lists/issues/54#issuecomment-125487293
if(i < dropIndex) {
dropIndex--;
}
// Remove this survey question from its original position
scope.survey_questions.splice(i, 1);
// Add this survey question to its new position
scope.survey_questions.splice(dropIndex, 0, question);
// Update the editQuestionIndex if applicable
if(typeof scope.editQuestionIndex === "number") {
// A question is being edited - lets see if we need to adjust the index
if(scope.editQuestionIndex === i) {
// The edited question was moved
scope.editQuestionIndex = dropIndex;
}
else if(scope.editQuestionIndex < i && dropIndex <= scope.editQuestionIndex) {
// An element that was behind the edit question is now ahead of it
scope.editQuestionIndex++;
}
else if(scope.editQuestionIndex > i && dropIndex >= scope.editQuestionIndex) {
// An element that was ahead of the edit question is now behind it
scope.editQuestionIndex--;
}
}
}
// Break out of the for loop
break;
}
scope.survey_name = "";
scope.survey_description = "";
questions = [] ;
scope.$emit('SurveySaved');
}
else{
scope.survey_name = "";
scope.survey_description = "";
url = GetBasePath('job_templates') + id + '/survey_spec/';
Rest.setUrl(url);
Rest.post({ name: scope.survey_name, description: scope.survey_description, spec: scope.survey_questions })
.success(function () {
// Wait('stop');
$('#survey-modal-dialog').dialog('close');
scope.$emit('SurveySaved');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to add new survey. POST returned status: ' + status });
});
}
};
//for toggling the input on password inputs
};
// return true here signals that the drop is allowed, but that we've already taken care of inserting the element
return true;
}
// Gets called when a user is creating/editing a question that has a password
// field. The password field in the form has a SHOW/HIDE button that calls this.
scope.toggleInput = function(id) {
// Note that the id string passed into this function will have a "#" prepended
var buttonId = id + "_show_input_button",
inputId = id,
buttonInnerHTML = $(buttonId).html();
if (buttonInnerHTML.indexOf("Show") > -1) {
$(buttonId).html("Hide");
if (buttonInnerHTML.indexOf("SHOW") > -1) {
$(buttonId).html("HIDE");
$(inputId).attr("type", "text");
} else {
$(buttonId).html("Show");
$(buttonId).html("SHOW");
$(inputId).attr("type", "password");
}
};
/* END QUESTION RELATED FUNCTIONS */
/* DELETE OVERLAY RELATED FUNCTIONS */
// This handles setting the delete mode and flipping the boolean used to show the delete overlay
scope.showDeleteOverlay = function(mode) {
// Set the delete mode (question or survey) so that the overlay knows
// how to phrase the prompt
scope.deleteMode = mode;
// Flip the deleteOverlayVisible flag so that the overlay becomes visible via ng-show
scope.deleteOverlayVisible = true;
}
// Called by the cancel/close buttons on the delete overlay. Also called after deletion has been confirmed.
scope.hideDeleteOverlay = function() {
// Clear out the delete mode for next time
scope.deleteMode = null;
// Clear out the index variable for next time
scope.questionToBeDeleted = null;
// Hide the delete overlay
scope.deleteOverlayVisible = false;
}
/* END DELETE OVERLAY RELATED FUNCTIONS */
// Watcher that updates the survey enabled/disabled tooltip based on scope.survey_enabled
scope.$watch('survey_enabled', function(newVal, oldVal) {
scope.surveyEnabledTooltip = (newVal) ? "Disable survey" : "Enable survey";
})
};
}
Init.$inject =
[ '$location',
'deleteSurvey',
[ 'deleteSurvey',
'editSurvey',
'addSurvey',
'GenerateForm',
@ -554,7 +542,6 @@ Init.$inject =
'Rest',
'ProcessErrors',
'$compile',
'finalizeQuestion',
'editQuestion',
'$sce'
'CreateSelect2'
];

View File

@ -8,60 +8,26 @@ export default
mode = (params.mode) ? params.mode : "survey-maker",
title = params.title,
element,
target = (mode==='survey-taker') ? 'password-modal' : "survey-modal-dialog",
buttons = [{
"label": "Cancel",
"onClick": function() {
scope.cancelSurvey(this);
},
"icon": "fa-times",
"class": "btn btn-default",
"id": "survey-close-button"
},{
"label": (mode==='survey-taker') ? "Launch" : "Save" ,
"onClick": function() {
setTimeout(function(){
scope.$apply(function(){
if(mode==='survey-taker'){
scope.$emit('SurveyTakerCompleted');
} else{
scope.saveSurvey();
}
});
});
},
"icon": (mode==='survey-taker') ? "fa-rocket" : "fa-check",
"class": "btn btn-primary",
"id": "survey-save-button"
}];
target = (mode==='survey-taker') ? 'password-modal' : "survey-modal-dialog";
CreateDialog({
id: target,
title: title,
scope: scope,
buttons: buttons,
width: 700,
height: 725,
width: 1200,
minWidth: 400,
draggable: false,
dialogClass: 'SurveyMaker-dialog',
onClose: function() {
$('#'+target).empty();
},
onOpen: function() {
Wait('stop');
if(mode!=="survey-taker"){
// if(scope.mode === 'add'){
// $('#survey-save-button').attr('disabled' , true);
// } else
if(scope.can_edit === false){
$('#survey-save-button').attr('disabled', "disabled");
}
else {
$('#survey-save-button').attr('ng-disabled', "survey_questions.length<1 ");
}
element = angular.element(document.getElementById('survey-save-button'));
$compile(element)(scope);
}
// Let the modal height be variable based on the content
// and set a uniform padding
$('#'+target).css({'height': 'auto', 'padding': '20px'});
if(mode==="survey-taker"){
$('#survey-save-button').attr('ng-disabled', "survey_taker_form.$invalid");
element = angular.element(document.getElementById('survey-save-button'));

View File

@ -0,0 +1,95 @@
<div id="survey-modal-dialog" style="display: none;">
<div class="SurveyMaker-deleteOverlay" ng-show="deleteOverlayVisible">
<div class="Modal-content modal-content">
<div class="Modal-header">
<div class="Modal-title" ng-bind="deleteMode === 'survey' ? 'Delete Survey' : (deleteMode === 'question' ? 'Delete Question' : '')"></div>
<div class="Modal-exitHolder">
<button class="close Modal-exit" ng-click="hideDeleteOverlay()">
<i class="fa fa-times-circle"></i>
</button>
</div>
</div>
<div class="Modal-body ng-binding">
<div class="Prompt-bodyQuery">Are you sure you want to delete this {{deleteMode}}?</div>
<div class="Prompt-bodyTarget">{{survey_questions[questionToBeDeleted].question_name}}</div>
</div>
<div class="Modal-footer">
<button ng-click="deleteMode === 'survey' ? deleteSurvey() : (deleteMode === 'question' ? deleteQuestion(questionToBeDeleted) : '')" class="btn Modal-footerButton ng-binding Modal-errorButton">DELETE</a>
<button ng-click="hideDeleteOverlay()" class="btn Modal-defaultButton Modal-footerButton">CANCEL</a>
</div>
</div>
</div>
<div class="SurveyMaker-header">
<div class="SurveyMaker-title">
<div class="SurveyMaker-titleText">{{name || "New Job Template"}}<div class="SurveyMaker-titleLockup"></div>SURVEY</div>
<div class="ScheduleToggle" ng-class="{'is-on': survey_enabled}" aw-tool-tip="surveyEnabledTooltip" data-placement="right" data-tip-watch="surveyEnabledTooltip">
<div ng-show="survey_enabled" class="ScheduleToggle-switch is-on" ng-click="toggleSurveyEnabled()">ON</div>
<div ng-show="!survey_enabled" class="ScheduleToggle-switch" ng-click="toggleSurveyEnabled()">OFF</div>
</div>
</div>
<button id="delete-survey" class="SurveyMaker-deleteButton" data-placement="top" ng-show="survey_exists" ng-click="showDeleteOverlay('survey')" aw-tool-tip="Delete survey" data-original-title="" title="">
<i class="fa fa-trash-o"></i>
</button>
<div class="SurveyMaker-close">
<button class="SurveyMaker-exit" ng-click="closeSurvey('survey-modal-dialog')"><i class="fa fa-times-circle"></i></button>
</div>
</div>
<div class="SurveyMaker-content">
<div class="SurveyMaker-questionPanel">
<div id="survey_maker_question_form"></div>
</div>
<div class="SurveyMaker-separatorPanel">
<div class="SurveyMaker-contentSeparator">
</div>
</div>
<div class="SurveyMaker-previewPanel">
<div style="display: flex; flex-direction: column; width: 100%;">
<div class="SurveyMaker-panelHeader">PREVIEW</div>
<div class="SurveyMaker-panelBody">
<div ng-if="survey_questions.length < 1" class="SurveyMaker-noQuestions">PLEASE ADD A QUESTION ON THE LEFT</div>
<ul dnd-list="survey_questions" class="SurveyMaker-previewRows" dnd-drop="surveyQuestionDropped(index, item)">
<li ng-repeat="question in survey_questions" dnd-draggable="question" dnd-effect-allowed="move" class="SurveyMaker-previewRow">
<dnd-nodrag>
<div>
<label class="SurveyMaker-previewLabel" for="question.variable">
<span class="label-text" ng-class="{'prepend-asterisk': question.required}"> {{question.question_name}}</span>
</label>
</div>
<div ng-if="question.question_description" class="col-xs-12 description">
<i>{{question.question_description}}</i>
</div>
<div class="SurveyMaker-previewInputRow">
<span dnd-handle class="SurveyMaker-reorderButton" data-placement="top" aw-tool-tip="Drag to reorder question" data-original-title="" title="">
<i class="fa fa-ellipsis-v"></i>
<span>&nbsp;</span>
<i class="fa fa-ellipsis-v"></i>
</span>
<survey-question class="SurveyMaker-previewInput" preview="true" question="question" ng-required="question.required" ng-disabled=true></survey-question>
<div class="SurveyMaker-previewActions">
<button class="List-actionButton" data-placement="top" ng-class="{'SurveyMaker-previewActions--selected' : editQuestionIndex == $index}" ng-click="editQuestion($index)" aw-tool-tip="Edit question" data-original-title="" title="">
<i class="fa fa-pencil"></i>
</button>
<button class="List-actionButton List-actionButton--delete" data-placement="top" ng-click="showDeleteQuestion($index)" aw-tool-tip="Delete question" data-original-title="" title="">
<i class="fa fa-trash-o"></i>
</button>
</div>
</dnd-nodrag>
</div>
</li>
<li class="SurveyMaker-previewRow dndPlaceholder">
Drop question here to reorder
</li>
</ul>
</div>
<div class="SurveyMaker-panelFooter">
<div class="Form-buttons">
<button id="survey-save-button" class="btn btn-sm Form-saveButton" ng-click="saveSurvey()" ng-disabled="survey_questions.length < 1 || !can_edit || editQuestionIndex !== null">SAVE</button>
<button id="survey-close-button" class="btn btn-sm Form-buttonDefault" ng-click="closeSurvey('survey-modal-dialog')">CANCEL</button>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,6 +0,0 @@
<!-- <div class="tab-pane" id="survey_maker"> -->
<div class="tab-pane" id="survey-modal-dailog">
<survey-maker ng-if="isModalReady"></survey-maker>
<div id="htmlTemplate" class="ng-scope"></div>
<!-- <div id="survey-modal-dialog" title="Add Survey"></div> -->
</div>

View File

@ -60,6 +60,8 @@ angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
beforeDestroy = params.beforeDestroy,
closeOnEscape = (params.closeOnEscape === undefined) ? false : params.closeOnEscape,
resizable = (params.resizable === undefined) ? true : params.resizable,
draggable = (params.draggable === undefined) ? true : params.draggable,
dialogClass = params.dialogClass,
forms = _.chain([params.form]).flatten().compact().value(),
buttons,
id = params.id,
@ -104,6 +106,8 @@ angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
title: title,
closeOnEscape: closeOnEscape,
resizable: resizable,
draggable: draggable,
dialogClass: dialogClass,
create: function () {
// Fix the close button
$('.ui-dialog[aria-describedby="' + id + '"]').find('.ui-dialog-titlebar button').empty().attr({'class': 'close'}).html('<i class="fa fa-times-circle"></i>');

View File

@ -615,7 +615,8 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
var element = params.element,
options = params.opts,
multiple = (params.multiple!==undefined) ? params.multiple : true,
placeholder = params.placeholder;
placeholder = params.placeholder,
customDropdownAdapter = (params.customDropdownAdapter!==undefined) ? params.customDropdownAdapter : true;
$.fn.select2.amd.require([
'select2/utils',
@ -632,14 +633,22 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
return Utils.Decorate(Adapter, Decorator);
}, Dropdown);
$(element).select2({
var config = {
placeholder: placeholder,
multiple: multiple,
containerCssClass: 'Form-dropDown',
width: '100%',
minimumResultsForSearch: Infinity,
dropdownAdapter: CustomAdapter
});
}
// multiple-choice directive calls select2 but needs to do so without this custom adapter
// to allow the element to be draggable on survey preview.
if(customDropdownAdapter) {
config.dropdownAdapter = CustomAdapter;
}
$(element).select2(config);
if(options){
for (var d = 0; d < $(element + " option").length; d++) {
var item = $(element + " option")[d];

View File

@ -261,7 +261,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
$('.form-control[required], input[type="radio"][required]').each(function () {
var label, span;
if (Empty($(this).attr('aw-required-when'))) {
label = $(this).parent().parent().find('label').first();
label = $(this).closest('.form-group').find('label').first();
if ($(this).attr('type') === 'radio') {
label = $(this).parent().parent().parent().find('label').first();
}
@ -568,7 +568,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
}
html += "<input type=\"text\" name=\"" + fld + "\" ";
html += "ng-model=\"" + fld + "\" ";
html += (field['class']) ? Attr(field, "class") : "";
html += " readonly />\n";
return html;
},
@ -674,7 +673,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "id=\"" + form.name + "_" + fld + "_chbox\" ";
html += (idx !== undefined) ? "_" + idx : "";
html += "class=\"";
html += (field['class']) ? field['class'] + " " : "";
html += "\"";
html += (field.trueValue !== undefined) ? Attr(field, 'trueValue') : "";
html += (field.falseValue !== undefined) ? Attr(field, 'falseValue') : "";
@ -737,7 +735,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "\">\n";
html += "<div class=\"alert";
html += (field.closeable === undefined || field.closeable === true) ? " alert-dismissable" : "";
html += (field['class']) ? " " + field['class'] : "";
html += "\" ";
html += (field.ngShow) ? this.attr(field, 'ngShow') : "";
html += ">\n";
@ -783,7 +780,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += buildId(field, fld, this.form);
html += (field.controlNGClass) ? "ng-class=\"" + field.controlNGClass + "\" " : "";
html += "class=\"form-control Form-textInput ";
html += (field['class']) ? " " + this.attr(field, 'class') : "";
html += "\" ";
html += (field.placeholder) ? this.attr(field, 'placeholder') : "";
html += (options.mode === 'edit' && field.editRequired) ? "required " : "";
@ -915,7 +911,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += (field.controlNGClass) ? "ng-class='" + field.controlNGClass + "' " : "";
html += "class='form-control Form-textInput";
html += (field['class']) ? " " + this.attr(field, 'class') : "";
html += "' ";
html += (field.chkPass) ? "chk-pass " : "";
@ -1044,7 +1039,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
html += "class=\"form-control Form-textArea";
html += (field['class']) ? " " + field['class'] : "";
html += (field['elementClass']) ? " " + field['elementClass'] : "";
html += "\" ";
html += (field.ngChange) ? this.attr(field, 'ngChange') : "";
@ -1083,7 +1077,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
html += "class=\"form-control Form-dropDown";
html += (field['class']) ? " " + field['class'] : "";
html += "\" ";
html += (field.ngOptions) ? this.attr(field, 'ngOptions') : "" ;
html += (field.ngChange) ? this.attr(field, 'ngChange') : "";
@ -1138,7 +1131,6 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
if (!field.slider && !field.spinner) {
html += "form-control";
}
html += (field['class']) ? " " + field['class'] : "";
html += "\" ";
html += (field.slider) ? "aw-slider=\"" + fld + "\" " : "";
html += (field.spinner) ? "aw-spinner=\"" + fld + "\" " : "";
@ -1387,14 +1379,16 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
// title and exit button
html += "<div class=\"Form-header\">";
html += "<div class=\"Form-title\">";
html += "<div class=\"Form-title ";
html += (this.form.titleClass) ? this.form.titleClass : "";
html += "\">";
html += (options.mode === 'edit') ? this.form.editTitle : this.form.addTitle;
if(this.form.name === "user"){
html+= "<span class=\"Form-title--is_superuser\" "+
"ng-if=is_superuser>Admin</span>";
}
html += "</div>\n";
if(options.cancelButton !== undefined && options.cancelButton === false) {
if(this.form.cancelButton !== undefined && this.form.cancelButton === false) {
html += "<div class=\"Form-exitHolder\">";
html += "</div>";
} else {
@ -1403,9 +1397,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "<i class=\"fa fa-times-circle\"></i>";
html += "</button></div>\n";
}
html += "</div>\n"; //end of Form-header
html += "</div>\n"; //end of Form-header
if (this.form.tabs) {
var collection;