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

Dylan Mann1
Giga Guru

I would inspect the page when you modal is open, and figure out what class is adding the padding or margin to your survey. You can update it right there to what you need it to be and then you'll know exactly where to add your code and what the style should be. 

find_real_file.png

Thanks Dylan,

 

Would you happen to know a way to set field styles depending on the field type? What I need is if a field on the widget is string/multiline.. the width needs to be 550 Px. I need to do it for all the fields on the widget and not just one and was wondering if there is a way to put a generic script that can do it using the client controller.

Could you share the portion of html where the fields are added to the screen? 

I know you can target css through an attribute, for example: 

.my-class[att='attribute'] {

    color: red; 

}

You might be able to target it that way, or worst comes to worst you can add an ng-class and conditionally add a class if the type is single or multiline text

Looks like it's contained in the sp-survey-form provider, the code might be hidden behind the scenes but you could probably find the html in the console's elements tab