logo

NJP

ServiceNow Service Portal: a 'my orders' widget

Import · Jan 07, 2021 · article

Contents

HTML. 1

CSS. 1

Client 1

Server. 1

**{{::c.options.title}}**

**{{::record.short\_description}}**
**${{{::data.msgs.current\_state}}}:** **{{record.stage}}**

0"** class\=**"sub-heading"**\> **{{addition.field\_label}}:** **{{addition.field\_value}}**

**${Reference}: {{record.number}}**
**${{{::data.msgs.current\_state}}}: {{record.stage}}**
**${Item}: {{record.item\_name}}**

${{{c.options.view_record_label}}}

data.showingCount"**\> **{{data.msgs.click\_more\_label}}**

<!-- .list-body -->

<!-- .panel-body -->

<!-- .panel -->

<!-- .container-fluid -->

**<**div **_class_** **\=**"accordian-panel"**\>** **<**div **_class_** **\=**"panel-heading"**\>** **<**div **_class_** **\=**"panel-title"**\>** **<**div tabindex**\=**"0" **_class_** **\=**"accordion-toggle" ng**\-**click**\=**"toggleOpen()" uib**\-**accordion**\-**transclude**\=**"heading" role**\=**"none"**\>** **<**span uib**\-**accordion**\-**header**\>** </span> </div> </div> </div> <div class\=**"panel-collapse collapse"** uib-collapse=**"!isOpen"**\> <div ng-transclude\></div> </div> </div>

CSS

/* Use with ng-class to selectively use in a theme */

.hide-border {

border: none;

}

/* Blending in the buttons with the panel-header */

.panel-button-row a.btn-primary.active {

background-color: #fff;

color: $brand-success;

}

/* Style the search bar nicely */

.search-bar {

width: 380px;

input[name="q"] {

border-top-left-radius:$search-border-radius;

border-bottom-left-radius:$search-border-radius;

color: $input-color !important;

}

span.input-group-btn {

button.btn-default {

border-top-right-radius:$search-border-radius;

border-bottom-right-radius:$search-border-radius;

border-left: none;

color: $brand-success;

}

}

input[name="q"] {

&::placeholder {

color: $input-color;

}

&::-ms-input-placeholder {

color: $input-color;

}

&:-ms-input-placeholder {

color: $input-color;

}

&::-webkit-input-placeholder {

color: $input-color;

}

&:-moz-placeholder {

color: $input-color;

}

&::-moz-placeholder {

color: $input-color;

}

}

}

/* Format the tabs into pills and other header stuff */

.list-header {

padding-left: 1em;

padding-right: 1em;

margin-bottom: 1em;

&>.nav-pills > li + li {

margin-left:1em;

}

&>.nav-pills > li {

font-size: 12px;

}

&>.nav-pills > li > h4 {

padding: 5px;

}

&>.nav-pills > li > a {

border-radius: $search-border-radius;

background-color: $search-border-color;

color: #000;

padding: 7px 12px;

top: 10px;

&:hover {

background-color: darken($search-border-color, 50%); /* Calculate a darker colour */

color: #fff;

}

}

&>.stats {

margin-top: 10px;

margin-bottom: 10px;

padding: 5px;

h4 {

color: #000;

display: inline;

.day-dropdown-container {

display:inline-block;

button {

margin-left: 5px;

background: $search-border-color;

height: 42px;

border:none;

color: #000;

.fa {

margin-left: 5px;

}

}

.dropdown-menu > li > a {

color: #000;

}

}

}

}

&>.nav-pills > li.active > a {

background-color: $brand-success;

color: #fff;

}

}

.item-icon {

max-width:30px;

height:auto;

}

.positive-stage {

color: $brand-info;

}

.accordian-panel {

background-color: #F2FBFE; /* TODO: Make into a CSS variable in the Theme */

border: none;

margin-bottom: 15px;

padding: 10px;

border-radius: $border-radius-base;

/* Using flex to distribute icons in accordian-header */

.flex-container {

display: flex;

flex-direction: row;

flex-wrap: nowrap;

justify-content: space-between;

height:unset;

width: unset;

.description-wrapper {

min-width: 250px;

}

span.description {

font-weight: 600;

color: $brand-primary;

text-decoration: underline;

}

.stage-container .fa {

margin-left: 10px;

color: $search-border-color;

}

.stage-container .stage-value {

font-weight: 600;

}

}

/* These are the contents of each panel (stage and button to view details) */

.record-details-wrapper {

background-color: #fff;

position: relative;

border-radius: $border-radius-large;

margin-top: 10px;

}

.sub-heading {

font-size: smaller;

.additional-field {

margin-right:5px;

.field-label {

font-weight: bold;

}

}

}

}

.show-more {

text-align: center;

a {

display:block;

}

}

.accordion-toggle {

padding: 17px;

&:focus {

outline-offset: 0;

}

}

Client

function ($scope*,* spUtil*,* $timeout*)* {

/* widget controller */

var c = this ;

$timeout*(* function () {

spUtil*.setBreadCrumb($scope,* [

{label*:* c*.data.myOrdersBreadcrumb,* url : '#'}

]);

});

c*.getFilterParam *=** function () {

return (c*.data.filter)* ? "&filter=" + c*.data.filter *:** "";

}

c*.getSortParam *=** function () {

return (c*.data.sort)* ? "&sort=" + c*.data.sort *:** "";

}

c*.getDaysAgoParam *=** function () {

return (c*.data.daysAgo)* ? "&daysago=" + c*.data.daysAgo *:** "";

}

// Dynamic binding to show/hide the day options

c*.dayDropdownStatus *=** {

isopen*:* false

}

// Dynamic binding used for the day dropdown label

c*.dayRange *=** "Last " + c*.data.daysAgo *+** " days";

}

Server

( function () {

// Prepare messages for template

data*.msgs *=** {};

data*.msgs.last_updated *=** gs*.getMessage("Last updated");*

data*.msgs.newest *=** gs*.getMessage("Created");*

data*.msgs.* status = gs*.getMessage("Status");*

data*.msgs.current_state *=** options*.current_state_label *||** "Current State";

data*.msgs.click_more_label *=** gs*.getMessage(options.click_more_label);*

data*.myOrdersBreadcrumb *=** gs*.getMessage(options.title);*

data*.search_options *=** "{title:'Search open orders', size: 'md', color: 'default', contextual_search_sources: '3d9d9d0edb8d6c10f04aad0505961925'}";

// Ensure to redirect to current or specified page when sorting

data*.currentPage *=** $sp*.getParameter("id");*

data*.stateField *=** options*.query_state_field *||** 'state';

var sortTypes = ['sys_updated_on', 'sys_created_on', 'status'];

data*.sort *=** (input && input*.sort)* || $sp*.getParameter("sort")* || "sys_updated_on";

if (!contains*(sortTypes,* data*.sort))*

data*.sort *=** "sys_updated_on";

var filterTypes = ['active', 'inactive'];

data*.filter *=** (input && input*.filter)* || $sp*.getParameter("filter")* || "active";

if (!contains*(filterTypes,* data*.filter))* {

data*.filter *=** "active";

}

var daysAgoOptions = ['7', '30'];

data*.daysAgo *=** (input && input*.daysAgo)* || $sp*.getParameter('daysago')* || "7";

if (!contains*(daysAgoOptions,* data*.daysAgo))* {

data*.daysAgo *=** "7";

}

var recordList = [];

data*.table *=** options*.query_table *||** "incident";

data*.queryLimit *=** options*.query_limit *||** 5*;*

data*.targetPage *=** options*.target_page *||** "ticket";

data*.activeQuery *=** options*.active_query *||** "active=true";

data*.inActiveQuery *=** options*.inactive_query *||** "active=false";

// Prep date range query string

var sDaysAgo = "", nDaysAgo = parseInt (data*.daysAgo);*

switch (nDaysAgo*)* {

case 7 :

sDaysAgo = "sys_created_onONLast 7 days@javascript:gs.beginningOfLast7Days()@javascript:gs.endOfLast7Days()";

break ;

case 30 :

sDaysAgo = "sys_created_onONLast 30 days@javascript:gs.beginningOfLast30Days()@javascript:gs.endOfLast30Days()";

break ;

default :

// TBD

}

var grRecord = new GlideRecord*(data.table);*

if (data*.filter=='active')* {

grRecord*.addEncodedQuery(data.activeQuery *+** sDaysAgo*);*

}

else {

grRecord*.addEncodedQuery(data.inActiveQuery);*

}

grRecord*.setLimit(data.queryLimit);*

if (data*.sort=='status')* {

grRecord*.orderByDesc(data.stateField);*

}

else {

grRecord*.orderByDesc(data.sort);*

}

grRecord*.query();*

while (grRecord*.next())* {

var targetRecord = {

number*:* grRecord*.getDisplayValue(),*

sys_id*:* grRecord*.getUniqueValue(),*

short_description*:* grRecord*.getValue('short_description')*

};

targetRecord*.stage *=** grRecord*.getDisplayValue(data.stateField);*

/* Catalogue specific attempt at getting an image for the list */

if (grRecord*.cat_item *&&** grRecord*.cat_item.icon.toString()* != '') {

targetRecord*.icon *=** grRecord*.cat_item.icon.toString()* + ".iix"

}

if (grRecord*.cat_item *&&** grRecord*.cat_item.icon.toString()* == '' && grRecord*.cat_item.picture.toString()* != '') {

targetRecord*.icon *=** grRecord*.cat_item.picture.toString()* + ".iix"

}

// Check if there are additional fields specified in the options and show them in the sub-header

targetRecord*.additionalFields *=** [];

if (options*.additional_fields *!=** '') {

var additionalFields = options*.additional_fields.split(',');*

for ( var a = 0*;* a < additionalFields*.length;* a*++)* {

var sField = additionalFields*[a];*

var oField = {

'field_label' : grRecord*[sField].getLabel(),*

'field_value' : grRecord*.getDisplayValue(sField)*

}

targetRecord*.additionalFields.push(oField);*

}

}

recordList*.push(targetRecord);*

}

data*.recordList *=** recordList*;*

data*.showingCount *=** recordList*.length;*

data*.recordCount *=** getCount*(data.table,* (data*.filter=='active')* ? data*.activeQuery+sDaysAgo *:** data*.inActiveQuery);*

if (data*.filter=='active')* {

data*.msgs.total_label *=** gs*.getMessage("{0} orders placed in",* [data*.recordCount])*

}

else {

data*.msgs.total_label *=** (options*.total_inactive_label *||** "Total closed records") + ": " + data*.recordCount;*

}

})();

function contains*(arr,* str*)* {

for ( var i = 0*;* i < arr*.length;* i*++)* {

if (arr*[i].equals(str))*

return true ;

}

return false ;

}

function getCount*(table,* query*)* {

var count = 0*,*

ga = new GlideAggregate*(table);*

ga*.addAggregate('COUNT');*

ga*.addEncodedQuery(query);*

ga*.query();*

if (ga*.next())* {

count = ga*.getAggregate('COUNT');*

}

return count*;*

}

View original source

http://www.cloudminus89.com/2021/01/service-portal-my-orders-widget.html