Feedback Widget on Service Portal

Manan4
Kilo Contributor

Hello everyone,

I am working on setting up a feedback button on the Service Portal that on-click, show a Pop-up box with a custom made Survey on it. The only things I am struggling with are the CSS portions of the widget. 

1. For the image scale question. The choice values aren't fully displayed.

2. Need to decrease the padding between the Modal window and the Survey itself.

This is what the survey looks like right now:

find_real_file.png

I have utilized the Feedback Button widget from the Store to get this done so far. This is what the widget looks like at the backend. 

Body HTML:

<script language="javascript" ng-if="data.redirectTarget" ng-init="c.redirect(data.redirectTarget)"></script>
<now-message key="Choose" value="${Choose}"/>
<now-message key="Invalid URL" value="${Invalid URL}"/>
<now-message key="Please fill all the fields with valid responses" value="${Please fill all the fields with valid responses}"/>
<now-message key="You must complete the required signature" value="${You must complete the required signature}"/>
<now-message key="All mandatory fields must be filled before submission" value="${All mandatory fields must be filled before submission}"/>
<now-message key="Something went wrong. Please try again later." value="${Something went wrong. Please try again later.}"/>

<!-- Spacing from top; just for mobile -->
<div class="sps-panel-spacer hidden-xs" />

<!-- Inline error messages visible on both layouts -->
<div class="row" ng-if="data.showInlineError && data.inlineErrorMessage && data.inlineErrorMessage.length">
	<div class="col-md-10 col-md-offset-1 col-sm-12">
		<div class="alert alert-danger" ng-repeat="msg in data.inlineErrorMessage track by $index">{{::msg}}</div>
	</div>
</div>

<div ng-if="!data.redirectTarget && !c.invalidSurvey">

	<div class="row" id="sp_survey_root">
		<div class="col-md-10 col-md-offset-1 col-sm-12">
			<div class="panel" ng-class="{ 'panel-default': !data.isKioskSurvey }">

				<!-- Survey Intro page -->
				<div class="wrapper-md text-center intro-text" ng-if="c.state == -1 && data.one_click_survey == 'false' && data.not_show_intro_note == 'false'">
                  	<div id="lang_picker" style="text-align: justify; text-align-last: right;">
                      <label ng-if="data.showLanguagePicker == 'true' && data.one_click_survey == 'false'" for="spLanguagePicker">${Language}: </label>
                      <select ng-if="data.showLanguagePicker == 'true' && data.one_click_survey == 'false'" ng-model="data.pickedLanguage" name="spLanguagePicker" id="spLanguagePicker"
                          ng-options="lang.label for lang in data.languages track by lang.value" ng-change="c.setLanguage()">
                      </select>
                  	</div>
					<div class="wrapper-sm">
						<div class="outer-circle">   
                            <span class = "intro-icon-assessment survey-icon glyphicon icon-article-document"
                                  ng-if="(data.evaluation_method == 'assessment' || data.evaluation_method == 'attestation_v2' || data.evaluation_method == 'risk_assessment')"></span>
                            <span ng-if="(data.evaluation_method == 'survey' || data.evaluation_method == 'quiz')" class="survey-icon glyphicon icon-form"></span>
						</div>
					</div>
					<div class="wrapper-sm">
                      <h3 class="title-label">{{::data.title}}</h3>
						<div ng-if="data.trigger_id" style="font-size:15px;">
                          {{::data.evaluation_method_display}} ${is in reference to} <a href="?id=ticket&table={{::data.trigger_table}}&sys_id={{::data.trigger_id}}">{{::data.trigger_display}}</a>
						</div>
						<div ng-if="data.trigger_desc" style="font-size:15px;">
							{{::data.trigger_desc}}
						</div>
						<div class="introduction" ng-if="data.introduction" style="font-size:15px;">
							<div ng-bind-html="c.trustHTML(data.introduction)"></div>
						</div>
					</div>

					<div class="row">
						<div class="col-xs-12 col-sm-6 col-sm-offset-3">
							<div class="wrapper-lg">
								<button class="btn btn-primary btn-block btn-lg" ng-click="c.startSurvey()">${Get Started}</button>
							</div>
						</div>
					</div>
				</div>

				<!-- Begin mobile-only -->
				<div class="visible-xs">
					<sp-survey-form c="c" data="data" pagination="'question'" is-mobile="true"></sp-survey-form>
				</div>
				<!-- End mobile only -->

				<!-- Begin desktop only -->
				<div class="hidden-xs">
					<sp-survey-form c="c" data="data" pagination="data.pagination"></sp-survey-form>
				</div>
				<!-- End desktop only -->

				<div class="sp-message-dialog" id="sign-auth-modal" style="display:none;position:fixed;top:10%" ng-if="data.one_click_survey == 'false'">
					<div class="modal-dialog modal-sm">
						<div class="modal-content">
							<form ng-submit="c.checkLogin()">
								<div class="modal-header">
									<button type="button" class="close" ng-click="c.hideSignatureAuthModal()" aria-label="Close"><span aria-hidden="true">&times;</span></button>
									<h4 class="modal-title">${Signature Authentication}</h4>
								</div>
								<div class="modal-body">
									<div ng-if="c.signAuthFailure" class="alert alert-danger text-center">${Invalid credentials}</div>
									<div class="row wrapper-xs">
										<div class="col-xs-4"><label class="wrapper-xs" for="sign-auth-username">${Username}</label></div>
										<div class="col-xs-8"><input ng-model="data.signature.username" class="form-control" id="sign-auth-username" type="text" name="sign-auth-username" disabled="true" /></div>
									</div>
									<div class="row wrapper-xs">
										<div class="col-xs-4"><label class="wrapper-xs" for="sign-auth-password">${Password}</label></div>
										<div class="col-xs-8"><input class="form-control" id="sign-auth-password" type="password" name="sign-auth-password" /></div>
									</div>
								</div>
								<div class="modal-footer">
									<button class="btn btn-primary" ng-click="c.checkLogin()">${OK}</button>
									<button class="btn btn-default" ng-click="c.hideSignatureAuthModal()">${Cancel}</button>
								</div>
							</form>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>
</div>

CSS:

.question-label, .category-label, .title-label, .description-text, .sps-checkbox-label, .sps-radio-label, .sps-scale-radio-label, .details-text, .assessable-record-text{
	color:black;
}

.assessable-record{
	margin-left : -1px;
  	margin-top : -1px;
    margin-right : -1px;  
  	background : #eff3f4;
  	padding:0px 0px;
}
.assessable-record-text{
	margin-left : -1px;
}

.category-description{
	margin-left: -1px;
    color : #e6e8ea;
	font-weight : 100;
}

.sp-attachment-block-single-line {
	margin-bottom:-1px;
}

.sps-panel-spacer{
  margin : -1px;
}

.survey-heading-text {
  	margin: -1px -1px -1px -1px;
}

.intro-text .survey-icon, .survey-heading {
	color:white;
}

.intro-text .survey-icon {
	font-size:4em;
}

.intro-text .outer-circle, .survey-heading {
	background: #3071a9;
}

.intro-text .outer-circle{
	padding: 0px 0px 0px 0px;
	display:inline-block;
	border-radius:100%;
}

.intro-icon-assessment{
  margin-left:-1px;
  margin-top:-1px;
}

.survey-heading{
  padding: 4px 4px;
  font-weight: 100;
}

.visible-xs .details-text {
	margin-bottom:-1px;
}

.visible-xs .input-group-checkbox {
	padding: 0 0px;
}

.sps-ranking-list li.hovered {
  cursor: move;
  background-color: #eee;
}

li.sortable-ghost {
	visibility: hidden;
}

.field-has-reference {
	padding-right:0 !important;
}

.add_info_no_padding {
	padding-left: 0;
}

.single_question_survey_image {
	display:flex;
    flex-wrap:wrap;
    justify-content:center;
}

.btn_one_click {
  border: none;
  background: none;
  font-weight:bold;
  word-break:break-word;
  white-space: normal;
}

.disable_button {
    color: #4D4D4D;
    opacity: 1;
}

.col_centered {
  float: none;
  margin: 0 auto;
}

.scale_center {
   text-align:left; 
   margin:auto; 
   width:1000px;
}

.img_size {
   height: 64px;
   width: 64px;
}
  
.tab_focus:focus {
  outline: thin dotted; 
  outline: 5px auto -webkit-focus-ring-color; 
  outline-offset: -2px; 
 }

@media screen and (orientation:landscape) and (min-width:48em) and (max-width:100em) {
 .col-md-2 {
   width: 1000px !important;
 }
}
  
div.introduction img, div.end-note img{
  max-width: 1000px;
  height: auto;
}
  

Server Script:

(function() {
	var page_id = options.id || $sp.getParameter('id');
	var typeId = options.type_id || $sp.getParameter('type_id');
	var surveyId = options.instance_id || $sp.getParameter('instance_id');
	var triggerId = options.instance_id || $sp.getParameter('sysparm_trigger_id') || "";
	var triggerTable = options.instance_id || $sp.getParameter('sysparm_trigger_table') || "";
	var kiosk = options.instance_id || $sp.getParameter('sysparm_kiosk') || false;
	var pickedLanguageId = $sp.getParameter('sysparm_lang') || "";
	if (kiosk && !(triggerTable && triggerId)) {
		gs.addErrorMessage(gs.getMessage("Something went wrong. Please try again later."));
		return false;
	}
	var refreshInterval = options.refresh_interval || $sp.getParameter('sysparm_refresh_interval');
	if (refreshInterval && typeId) {
		refreshInterval = parseInt(refreshInterval, 10);
		if(!isNaN(refreshInterval))
			data.refreshInterval = refreshInterval;
	}
	data.triggerTable = triggerTable;	
	data.triggerId = triggerId;
	data.pickedLanguageId = pickedLanguageId;
	data.showLanguagePicker = "false";
	var showLangProperty = gs.getProperty("show_lang_picker_for_publicsurveys");

	var sp = new SPSurveyAPI();
	if (page_id == 'public_survey') {
		if (!!typeId && sp.isSurveyPublic(typeId) && showLangProperty == "true") {
			// If it is a public survey and user is not logged in and show language picker property is marked
			// Check if picked language equals to session language
			// If not same, change session language
			data.showLanguagePicker = "true";
			var session = gs.getSession();
			var sessionLang = session.getLanguage();
			if (data.pickedLanguageId && sessionLang !== data.pickedLanguageId) {
				var amtUtil = new global.AssessmentUtils();
				var isSuccess = amtUtil.setSessionLanguage(data.pickedLanguageId);
				if (!isSuccess) {
					gs.addErrorMessage(gs.getMessage("Language doesn't exist."));
					return false;
				}
				// After changing session language, we need to refresh page again to see the system has been entriely translated.
				data.redirectTarget = "?id=public_survey&type_id="+typeId+"&sysparm_lang="+data.pickedLanguageId+(surveyId ? "&instance_id="+surveyId : "");
			} else
				data.sessionLanguage = sessionLang;
		} else {
			if (!!typeId && (!sp.isSurveyPublic(typeId) || gs.isLoggedIn()))
				data.redirectTarget = "?id=take_survey&type_id=" + typeId;
			else if (!!surveyId && (!sp.isSurveyInstancePublic(surveyId) || gs.isLoggedIn()))
				data.redirectTarget = "?id=take_survey&instance_id=" + surveyId;
		}
	}
	
	sp.loadSurvey(typeId, surveyId, data);

	if(input){
		if (input.action == "check_string_validation") {
			data.stringValidationResponse = new global.SurveyUtilAjax().validateMultipleStringFields(input.string_qa_map);
		}
	}
})();

Client Controller:

function($rootScope,$sce,$http, $scope, $anchorScroll, i18n, nowAttachmentHandler, $timeout, $location, spUtil, $window) {
	// widget controller
	var c = this;

	// DEF0073704: listen to window's width. 
	// Every time width got changed, we should update c.state accordingly for either desktop view or mobile view.
	c.windowInnerWidth = $window.innerWidth;
	c.firstQuestionIndex = $window.innerWidth <= 767 ? 0 : 1;
	angular.element($window).bind('resize', function(){
		c.windowInnerWidth = $window.innerWidth;

		// Manually calling $digest is required because resize event is outside of angular
		scope.$digest();
	});
		
	if ($rootScope.user && !$rootScope.user.date_format) 
		$rootScope.user.date_format = c.data.system_date_format;

	$timeout(function() {
		if(c.data.parameterizedMsgsMap && c.data.parameterizedMsgsMap.title && c.data.parameterizedMsgsMap.title.length > 0){
			var bc = [{label: c.data.parameterizedMsgsMap.title, url: '#'}];
			$rootScope.$broadcast('sp.update.breadcrumbs', bc);
		}
	});
	var searchObject = $location.search();
	c.isPublic = ('id' in searchObject) && searchObject['id'].indexOf('public_survey') == 0;
	c.state = -1;
	if(c.data.one_click_survey == 'true')
		c.state = 0;
	
	// Skip introduction note page
	if (c.data.not_show_intro_note == 'true')
		c.state = 0;
	
	c.startSurvey = function() {
		c.state = 0;
		// After click "get started", we should scroll page to the first question of the survey
		$location.hash("sp_survey_root");
		$anchorScroll();

		// DEF0087934: We automatically focus on the first question of the page after clicking "Get Started".
		c.focusOnFirstQuestion(c.firstQuestionIndex);
	};

	// The first question of every page on mobile view is $('div.wrapper-sm.row.ng-scope.form-group')[0]
	// The first question of every page on desktop view is $('div.wrapper-sm.row.ng-scope.form-group')[1]
	c.focusOnFirstQuestion = function(index) {
		$timeout(function() {
			$('div.wrapper-sm.row.ng-scope.form-group')[index].focus();
		});
	};

	c.trustHTML = function(string) {
    return $sce.trustAsHtml(string);
	};
	
	c.redirect = function(target) {
		window.location = target;
	};
	
	c.setLanguage = function() {
		// Setup session langugae for current user
		$scope.c.data.pickedLanguageId = $scope.c.data.pickedLanguage.value;
		var surveyInstanceId = $scope.c.data.instanceId;
		if ($window.location.href.indexOf("sysparm_lang") !== -1) {
			var index = $window.location.href.indexOf("sysparm_lang");
			$window.location.href = window.location.href.replaceAt(index, "sysparm_lang=" + $scope.c.data.pickedLanguageId);
		} else
			$window.location.href +=  "&sysparm_lang=" + $scope.c.data.pickedLanguageId + "&instance_id=" + surveyInstanceId;
	};

	String.prototype.replaceAt = function(index, replacement) {
		return this.substr(0, index) + replacement+ this.substr(index + replacement.length);
	};
	
	// The picked language equals to either session language or the language picked in previous time
	if ($scope.c.data.languages) {
		if (!$scope.c.data.pickedLanguageId){
			for (var i = 0; i < $scope.c.data.languages.length; i ++) {
				if ($scope.c.data.languages[i].value == $scope.c.data.sessionLanguage) {
					$scope.c.data.pickedLanguage = $scope.c.data.languages[i];
					break;
				}
			}
		} else {
			for (var i = 0; i < $scope.c.data.languages.length; i ++) {
				if ($scope.c.data.languages[i].value == $scope.c.data.pickedLanguageId) {
					$scope.c.data.pickedLanguage = $scope.c.data.languages[i];
					break;
				}
			}
		}
	}

	// Mark all categories as non collapsed
	if (!!c.data.categories && !!c.data.categories.idList && !!c.data.categories.idList.length)
		c.data.categories.idList.forEach(function(id){
			c.data.categories.idMap[id].collapsed = false;
		});

	c.setupAttachmentHandler = function(field){
		if (field.attachmentHandler)
			return;

		field.attachmentHandler = new nowAttachmentHandler(attachSuccess, appendError);

		function attachSuccess(attachments, action) {
			field.attachments = attachments;
		}

		function appendError(error) {
			c.showInlineErrorMessage(error);
		}

		$timeout(function() {
			field.attachmentHandler.setParams('asmt_assessment_instance_question', field.sys_id);
			field.attachmentHandler.getAttachmentList();
		})
	}
	
	c.isDependencySatisfied = function(field) {
		return c.isDependencySatisfiedHelper(field, false);
	};
	
	//for the form rendering, since we need to load all questions/dependency fields and toggle based on selection we are ok with any one of the filed is satisfied.
	c.isAnyDependencySatisfied = function(field) {
		var self = this;
		if (field.type == 'template') {
			var group = c.data.templateGroups[field.templateGroup];
			return group && group.questions && group.questions.reduce(function(x, y) {return x || c.isDependencySatisfiedHelper(c.data.questions.idMap[y])}, false);
		}
		return c.isDependencySatisfiedHelper(field, true);
	};

	c.isDependencySatisfiedHelper = function(field, checkAny) {
		if (!field.depends_on)
			return true;

		var retVal = false;
		var dependencyId;
		
		if(c.data.evaluation_method == 'assessment' 
		   || c.data.evaluation_method == 'attestation_v2' 
		   || c.data.evaluation_method == 'risk_assessment'){
				dependencyId = c.data.questions.metricMap[field.depends_on+field.source_id];
		}
		else{
			dependencyId = c.data.questions.metricMap[field.depends_on];
		}
		
		var dependency = c.data.questions.idMap[dependencyId];

		switch (dependency.type) {
			case 'scale':
			case 'choice':
			case 'template':
			case 'imagescale':
			case 'numericscale':
				var displayedWhen = (dependency.type != 'template') ? field.displayed_when.split(',') : field.displayed_when_template.split(',');
				var choices = (dependency.type == 'template') ? dependency.template.choices : dependency.choices;
				var selectedChoice = choices.filter(function(choice){return dependency.value == choice.value})[0];

				if (!selectedChoice)
					break;

				displayedWhen.forEach(function(sys_id) {
					if (selectedChoice.sys_id == sys_id)
						retVal = true;
				});
				break;
			case 'multiplecheckbox':
				var selectedChoices = dependency.choices.filter(function(choice) {return choice.selected}).map(function(c){return c.sys_id});
				var displayedWhen = field.displayed_when.split(',');

				displayedWhen.forEach(function(sys_id) {
					selectedChoices.forEach(function(choiceId) {
						if (choiceId == sys_id)
							retVal = true;
					});
				});
				break;
			case 'boolean':
				retVal = (dependency.value == field.displayed_when_yesno);
				break;
			case 'checkbox':
				var displayedWhen = (field.displayed_when_checkbox == '1') ? 'true' : 'false';
				retVal = (dependency.value == displayedWhen);
				break;
			default:
				break;
		}

		return retVal && (checkAny ? c.isAnyDependencySatisfied(dependency) : c.isDependencySatisfied(dependency));
	};

	c.showSignatureMessage = function() {
		c.showInlineErrorMessage(i18n.getMessage("You must complete the required signature"));
	};

	c.showMandatoryMessage = function() {
		c.showInlineErrorMessage(i18n.getMessage("All mandatory fields must be filled before submission"));
	};

	c.showInvalidResponseMessage = function() {
		c.showInlineErrorMessage(i18n.getMessage("Please fill all the fields with valid responses"));
	};

	c.hideInlineErrorMessage = function() {
		c.data.showInlineError = false;
	};

	c.isMandatoryFilled = function(field) {
		if (!field)
			return true;
		
		// for mandatory check we cannot use the isDepndencySatisfied because we have to check each individual dependency field requiredness only cannot use the cumulative result of all questions,
		// which is being returned in isDependencySatisfied, however for display purpose that is a valid way of checking.
		if (!!field.depends_on && field.depends_on.length == 32 && !c.isDependencySatisfiedHelper(field))
			return true;

		if (field.type == 'multiplecheckbox')
			return field.choices.map(function(choice) {return choice.selected}).reduce(function(x, y) {return x || y}, false);

		if (field.type == 'attachment')
			return field.attachments && field.attachments.length > 0;

		if (field.type == 'ranking')
			return field.choices.map(function(choice) {return !!choice.value && choice.value != '-1'}).reduce(function(x, y) {return x && y}, true);

		if (field.type == 'boolean')
			return field.value == 0 || field.value == 1 || field.value == -1;

		var fieldValue = (field.type != 'template') ? field.value : c.data.questions.idMap[field.sys_id].value;
		return fieldValue != null && fieldValue != undefined && fieldValue != '';
	};

	c.showInlineErrorMessage = function(msg) {
		if (typeof(msg) == 'string')
			c.data.inlineErrorMessage = [msg];
		else
			c.data.inlineErrorMessage = msg;

		c.data.showInlineError = true;
		$('section.page').scrollTop(0);
	};
    
	var errorMessage = '';
	if (!c.data.questions || !c.data.questions.idList || !c.data.questions.idList.length) {
		c.showInlineErrorMessage(i18n.getMessage("Invalid URL"));
		c.invalidSurvey = true;
		return;
	}

	if (c.data.state == 'complete' && !c.data.can_retake) {
		errorMessage = c.data.parameterizedMsgsMap.already_completed;
		c.showInlineErrorMessage(errorMessage);
		c.invalidSurvey = true;
	}
	
	//Throw error message for canceled state
	if (c.data.state == 'canceled') {
		errorMessage = c.data.parameterizedMsgsMap.cancel_msg;
		c.showInlineErrorMessage(errorMessage);
		c.invalidSurvey = true;
	}

	c.updateQuestion = function(questionId, formdata) {
		var question = c.data.questions.idMap[questionId];
		if (!c.isDependencySatisfied(question)) {
			// Clear out any selected choices
			if (question.type != 'multiplecheckbox' &&  question.type != 'ranking' && question.type != 'attachment' ) {
				formdata[question.name] = '';
				formdata["ADDINFO:" + question.name] = '';
			} else if (question.type == 'attachment') {
				if (!!question.attachments)
					question.attachments.forEach(function(attachment) {
						question.attachmentHandler.deleteAttachment(attachment);
					});
				formdata["ADDINFO:" + question.name] = '';
			} else {
				var prefix = question.type == 'multiplecheckbox'?'ASMTDEFINITION:':'ASMTDEFINITIONRANK:';
				question.choices.map(function(choice) {
					var key = prefix + choice.sys_id + '_' + question.metric+'_'+ question.source_id;
					if(question.type == 'multiplecheckbox')
						formdata[key] = '';
					else
						formdata[key] = '';
					formdata["ADDINFO:" + key] = '';
				});
			}
			return;
		}

		if (question.type != 'multiplecheckbox' &&  question.type != 'ranking' ) {
			formdata[question.name] = question.value;
			formdata["ADDINFO:" + question.name] = question.add_info;
		} else {
			var prefix = question.type == 'multiplecheckbox'?'ASMTDEFINITION:':'ASMTDEFINITIONRANK:';
			question.choices.map(function(choice) {
				var key = prefix + choice.sys_id + '_' + question.metric+'_'+ question.source_id;
				if(question.type == 'multiplecheckbox')
					formdata[key] = (choice.selected || choice.selected == 'true') ? true : '';
				else
					formdata[key] = choice.value;
				formdata["ADDINFO:" + key] = question.add_info;
			});
		}
	};

	c.submitAjax = function(formdata) {
	
		// first stringify the formData
		var stringifiedFormData = JSON.stringify(formdata);
		// then encode the stringified form data to handle any special characters and pass this to processor
		// this will be decoded in processor
		var encodedFormData = encodeURIComponent(stringifiedFormData);
		
		$http({
			method: 'POST',
			url: '/sp_survey.do',
			data: encodedFormData,
			headers: {
				'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
			}
		})
		.success(successHandler);

		function successHandler(responseData) {
			if (responseData.success === true || responseData.success === 'true') {
				var conclusion = c.data.parameterizedMsgsMap.submitted_msg;
				c.data.conclusion = (c.data.conclusion && c.data.conclusion.length > 0) ? c.data.conclusion : conclusion;
				if (formdata.sysparm_action == 'submit' || formdata.updateSuccessMessage) {
					if (formdata.sysparm_action == 'submit') {
						if (c.data.refreshInterval && c.data.refreshInterval > 0) {
							c.data.successMessage = c.data.conclusion;
							$timeout(function() {
								$window.location.reload();
							}, c.data.refreshInterval * 1000);						
						} else if (c.data.redirect_url && c.data.redirect_url.length > 0)
							 window.location.href = c.data.redirect_url;	
						else
							 c.data.successMessage = c.data.conclusion;
					} else if (formdata.sysparm_action == 'save')
						c.data.successMessage = c.data.parameterizedMsgsMap.saved_msg;
					else if (formdata.sysparm_action == 'cancel')
						c.data.successMessage = c.data.parameterizedMsgsMap.not_saved_msg;
				}
			} else {
				c.showInlineErrorMessage(i18n.getMessage("Something went wrong. Please try again later."));
				c.invalidSurvey = true;
			}

			c.data.showMessage = true;
		}
	};

	c.getPercentAnswered = function() {
		var totalCount = 0;
		var totalAnswered = 0;
		c.data.questions.idList.forEach(function(qid) {
			var question = c.data.questions.idMap[qid];
			if (c.isDependencySatisfied(question)) {
				totalCount++;
				if (question.type == 'multiplecheckbox' && question.choices.reduce(function(x, y) {return x || y.selected;}, false)) 
					totalAnswered++;
				else if (question.type == 'ranking' && question.choices.reduce(function(x, y){return x && !!y.value;}, true)) 
					totalAnswered++;
				else if (question.type == 'attachment' && !!question.attachments && question.attachments.length > 0) 
					totalAnswered++;
				else if (!!question.value) 
					totalAnswered++;
			}
		});

		return (totalAnswered * 100.0) / totalCount;
	};

	c.showSignatureAuthModal = function() {
		c.signAuthFailure = false;
		var modal = jQuery('#sign-auth-modal');
		var left = (window.innerWidth - parseInt(modal.css('width'))) / 2;
		left = left ? (left + 'px') : '25%';
		modal.css('display', 'block').css('left', left);
	};

	c.hideSignatureAuthModal = function() {
		if ($scope.c.data.signature.validationCallback && c.signAuthFailure) {
			$scope.c.data.signature.validationCallback(false);
		}
		jQuery('#sign-auth-modal').css('display', 'none');
	};

	c.checkLogin = function() {
		c.signAuthFailure = false;
		$http({
			method: 'POST',
			url: '/sp_survey.do',
			data: {
				sysparm_request_type: 'verify_signature',
				sysparm_user: jQuery('#sign-auth-username').val(),
				sysparm_password: jQuery('#sign-auth-password').val()
			}
		}).then(function (response) {
			if (response.data == 'true') {
				c.hideSignatureAuthModal();
				$scope.c.data.signature.validated = true;
				if ($scope.c.data.signature.validationCallback) {
					$scope.c.data.signature.validationCallback(true);
					$scope.c.data.signature.validationCallback = null;
				}
				return;
			}
			c.signAuthFailure = true;
			return false;
		});
	}
}

 

Any help/advise would be much appreciated. Thank you.

1 ACCEPTED SOLUTION

Ok so "role" looks promising. Try adding some CSS like

 

textarea[role="textbox"] {
   width: 550px;
} 

You may not even need that, if there's a specific enough class you might be able to do something like:

.survey-class > textarea {
    width: 550px;
}

The trick is making it specific enough so you don't affect other elements unintentionally. I don't think there are any other variables besides multiline text with a type of textarea though. The good thing is you've got options.

 

View solution in original post

16 REPLIES 16

Thats right, find_real_file.png

 

Any thoughts/ideas?

Ok so "role" looks promising. Try adding some CSS like

 

textarea[role="textbox"] {
   width: 550px;
} 

You may not even need that, if there's a specific enough class you might be able to do something like:

.survey-class > textarea {
    width: 550px;
}

The trick is making it specific enough so you don't affect other elements unintentionally. I don't think there are any other variables besides multiline text with a type of textarea though. The good thing is you've got options.

 

The below option worked!!!!

textarea[role="textbox"] {
   width: 550px;
} 

 

Thank you so much. Really appreciate your guidance and quick response. Cheers!

Anytime! If you don't mind, mark the Solution as Accepted to close out the question and help me on my journey to ServiceNow Community MVP

Thanks!

100%. Right before I close the question, could you also advise to what the best way to set padding between the modal and the survey form. This the below element that works behind it, I tried something like

.ng-scope{

padding: 0px} but I dont think worked. find_real_file.png