måndag 15 februari 2016

Part 3 - Giving the service status a nicer look.

On the last part I build the page without giving to much into how the whole thing looked. Now I had some time to look into this and also taken some help from colleagues with more eye for design than I will ever have. I also thrown in some HTML-fields and client script to automate the function of Valid to. This is how I did it.


This is how it looks now

So what have I been done up to?

Well, I have gone through all code and slimmed it down a few notches and put all of the styling in a style sheet instead. To make it easy I will put down the whole code here and just explain what have been done. In this post I will put in the code as if it is on a CMS site. If you just use a UI Page you need to call the style sheet so it will be applied on the UI Page. You can do this the same way I did on the UI Page for "new feature". You can read about it here: Making your own "new Feature" window

I've also made a new html-field on the records that populates this page. This making the ServiceDesk able to style the description of each record how they want, and throw in a picture or else. Not just having it as a plain text like before. 

Last thing is that when someone changes the type from "Active Incident" to "resolved incident" it will go to our schedule and set the valid to date 2 days ahead. Using schedule so if doing this on a Friday it won't be going to the history tab on Sunday.

New field on the form:

First thing to do is to create a new HTML-field on the form for the records. I named it "u_deschtml" and you will see that I refer to it below. I have also hidden the former field "Description" so they only use this field.

Here is the Dynamic block( or UI Page):

This is pretty where most of the work have been done. We changed how the tabs looks and how the information is placed. Mostly done with a few lines of CSS. The biggest issue was to learn how to remove the CSS that comes OOB from ServiceNow and get the page to use ours instead.

You can se we use the ng-bind to get the html-field (deschtml) to be rendered in html and not just the populate the field with the html-code. We had some issues with other solution not working in Chrome but this one does.

Here is the XML:

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
  <!-- 
Import our own libraries -->
    <g:requires name="angular.min.jsdbx" />
<g:requires name="angular-sanitize.min.jsdbx" />
<g:requires name="jquery.min.jsdbx" />
<g:requires name="bootstrap.min.jsdbx" />
<!-- 
This UI Script handles our "client script" for angularJS etc.
-->
<g:requires name="driftInformation.jsdbx" />


<!-- 
The User Interface 
There is a style sheet for this page
-->

<!-- 
Overhead text and tabs
-->
<div>
<h3>Operation Information</h3>
<p>Information about current disruptions (incidents) and upcoming events. Click on the heading to see more detailed information.</p>
<ul class="nav nav-pills nav-justified" role="tablist">
<li class="active"><a class="ldctablink" href="#driftinfo">Current</a></li>
<li><a class="ldctablink" href="#history">History</a></li> 
</ul>
</div> 

<!--
Code to handle each post. Made with AngularJS and is for both tabs
-->
<div id="activeinfo" ng-app="descEditor">
<div class="tab-content">
<div id="driftinfo" class="tab-pane fade in active">
<div ng-controller="DescriptionText" data-ng-init="getAllActive()">
<div class="panel-group" id="accordion">
<div class="panel panel-{{num.u_type}}" ng-repeat="num in numbers | orderBy: '-order'">
<div class="panel-heading">
<a class="collapselink" data-toggle="collapse" data-parent="#accordion" href="#{{num.number}}">
<div class="drifttable">
<div class="drifttbrow">
<div class="drifttbcelleft">
<h4 class="panel-title"><b>{{num.short_description}}</b></h4>
</div>
<div class="drifttbcellright">
<span class="updatecreate">Created: {{" " + num.sys_created_on}}</span>
</div>
</div>
<div class="drifttbrow">
<div class="drifttbcelleft">
<span class="updatecreate"><b>{{" " + num.statuscode}}</b></span>
</div>
<div class="drifttbcellright">
<span class="updatecreate">Updated: {{" " + num.sys_updated_on}}</span>
</div>
</div>
</div>
</a>
</div>
<div id="{{num.number}}" class="panel-collapse collapse">
<div class="panel-body">
<div ng-bind-html="get_pre(num.deschtml)"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="history" class="tab-pane fade">
<div ng-controller="DescriptionText" data-ng-init="getAllHistory()">
<div class="panel-group" id="accordion2">
<div class="panel panel-{{num.u_type}}" ng-repeat="num in numbers | orderBy: '-sys_updated_on'">
<div class="panel-heading">
<a class="collapselink" data-toggle="collapse" data-parent="#accordion2" href="#{{num.number}}">
<div class="drifttable">
<div class="drifttbrow">
<div class="drifttbcelleft">
<h4 class="panel-title"><b>{{num.short_description}}</b></h4>
</div>
<div class="drifttbcellright">
<span class="updatecreate">Created: {{" " + num.sys_created_on}}</span>
</div>
</div>
<div class="drifttbrow">
<div class="drifttbcelleft">
<span class="updatecreate"><b>{{" " + num.statuscode}}</b></span>
</div>
<div class="drifttbcellright">
<span class="updatecreate">Updated: {{" " + num.sys_updated_on}}</span>
</div>
</div>
</div>
</a>
</div>
<div id="{{num.number}}" class="panel-collapse collapse">
<div class="panel-body"><div ng-bind-html="get_pre(num.deschtml)"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</j:jelly>

For this we need the Style Sheet:

For most of the people out here I don't think this is any advanced stuff. I'm still deep down on the learning curve here, but it's amazing what you can do. Hopefully will the comments in the code explain enough what it's for. If you see anything that I have overdone or could do better in another way, please let me know. 

Style Sheet:

/** LINKS ON EACH PANEL **/
.collapselink:hover {
text-decoration: none;
}
.collapselink:active {
text-decoration: none;
}
.collapselink:focus {
outline: 0;
text-decoration: none;
}
/** COLORS ON PANELS**/
.panel-primary {
background-color: #daecf1;
}
.panel-success {
background-color: #c6ecd9;
}
/** LAYOUT OF TABS **/
.ldctablink:visited {
background-color: #daecf1 !important;
color: black !important;
}
/**TABLE **/
.drifttable {
display: table; 
width: 100%; 
}
.drifttbrow {
display: table-row;
}
.drifttbcelleft {
display: table-cell; 
text-align: left;
}
.drifttbcellright {
display: table-cell; 
text-align: right;
width: 30%; 
}
.updatecreate {
font-size: 8pt;
color: grey;
}
.panel-body {
background-color: white !important;
}

/**TO MAKE TABS SHOW CORRECT COLOR IN IE **/
.active .ldctablink:link {
background-color: #daecf1 !important;
color: black !important;
}
/** REMOVE DEFAULT MARGIN-BOTTOM ON PANEL**/
.panel-group {
margin-bottom: 0px !important;
}

The UI Script with Angular code:
This is the UI Script driftinformation which we call for in the dynamic block.

Here is one part new and that is the method to handle the html-field. We are using the ng-sanitize for this and you can see it under the get_pre function below

Script:
var myApp = angular.module('descEditor', ['ngSanitize']);

myApp.controller('DescriptionText', ['$scope','$sce', function($scope, $sce) {


$scope.getAllActive = function () {
var ga = new GlideAjax('getDriftInformation');
ga.addParam('sysparm_name','getinfo');
ga.addParam('sysparm_tab','Aktuellt');
ga.getXML(showDrift);

};
$scope.getAllHistory = function () {
var ga = new GlideAjax('getDriftInformation');
ga.addParam('sysparm_name','getinfo');
ga.addParam('sysparm_tab','Historik');
ga.getXML(showDrift);

};

$scope.get_pre = function(x){

return $sce.trustAsHtml(x);
        };

function showDrift(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
$scope.$apply(function () {
$scope.numbers= angular.fromJson(answer);
});
}


}
] );

$(document).ready(function(){
$(".nav-pills a").click(function(){
$(this).tab('show');
});

});

Here is the Script include:

This looks pretty much the same, what has been added is the method to calculate which Valid to date that should be set when a record is changed to type "Resolved Incident". There is a Client Script using this.

Script:

var getDriftInformation = Class.create();
getDriftInformation.prototype = Object.extendsObject(AbstractAjaxProcessor, {

getinfo: function() {

var info = [];
//See what tab that is requesting records
var tab = this.getParameter('sysparm_tab');
//Choose which queryString that should active
if (tab == 'Aktuellt')
var queryString = "u_valid_to>=javascript:gs.daysAgoStart(0)^ORu_valid_toISEMPTY";
else
var queryString = "u_valid_to<javascript:gs.daysAgoStart(0)";

var gr = new GlideRecord('u_driftinformation');
gr.addEncodedQuery(queryString);
gr.query();

while(gr.next()) {
var svar = {};
svar.number = gr.number.toString();
svar.short_description = gr.short_description.toString();
svar.u_type = gr.u_type.toString();
svar.description = gr.description.toString();
//Using substr to remove seconds from the dateTime variables
svar.sys_updated_on = gr.sys_updated_on.getDisplayValue().substr(0,16).toString();
svar.sys_created_on = gr.sys_created_on.getDisplayValue().substr(0,16).toString();
svar.order = gr.order.toString();
svar.deschtml = gr.u_deschtml.toString();
svar.statuscode = gr.u_type.getDisplayValue();
info.push(svar);

}
return new global.JSON().encodeArray(info);
},

setValidTo: function() {

gs.include('DurationCalculator');

var dc = new DurationCalculator();
//Get the relative duration for 2 days
var relDur = '3bfa9bd10a0a0b5200c18037fbaa9a2c';
//Load the schedule into our calculation
addSchedule(dc);

//Do the calculation and return the end dateTime.
dc.setStartDateTime(gs.nowDateTime());
    if (!dc.calcRelativeDuration(relDur)) {
        gs.log("*** calcRelativeDuration failed");
        return;
    }
    return dc.getEndDateTime();

function addSchedule(durationCalculator) {
    //  Load the "8-5 weekdays excluding holidays" schedule into our duration calculator.
    var scheduleName = "LDC 8-17 vardagar";
    var grSched = new GlideRecord('cmn_schedule');
    grSched.addQuery('name', scheduleName);
    grSched.query();
    if (!grSched.next()) {
        gs.log('*** Could not find schedule "' + scheduleName + '"');
        return;
    }
    durationCalculator.setSchedule(grSched.getUniqueValue(), "GMT");
}


},
//This is needed to be able to use this script include on a public page.
isPublic: function() {
   return true;
  }
});

Last Out is the client script:

The is a new function that I didn't have at all on my "Part 2". This script calls on the method "setValidTo" from the script include above and then sets the value when type is changed to "Resolved Incident".

It's a simple onChange on the field "Type" and the code looks like this:

function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue == '') {
      return;
    };
if (newValue == 'success') {

var ga = new GlideAjax('getDriftInformation');
ga.addParam('sysparm_name','setValidTo');
ga.getXML(setValid);
}

function setValid(response) {
var answer = response.responseXML.documentElement.getAttribute('answer');
g_form.setValue('u_valid_to',answer);
}
}


Well this is all for now. I'll return with the final part where I'm hoping automate some more to make it easier to handle direct from incidents and for example set "type" to resolved incident when the incident it self goes to resolved.

And if you missed part 1 & 2, here they are:
Part 1: AngularJS, JSON & public UI Pages. How do I get it all together?

Part 2: Making UI Page "Service Status" with AngularJS

Inga kommentarer:

Skicka en kommentar