-
Notifications
You must be signed in to change notification settings - Fork 1
/
Ugly.txt
88 lines (77 loc) · 22.5 KB
/
Ugly.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
<!DOCTYPE html>
<html>
<head>
<title>CATS-Timebox Misalignment Notifier-0.1</title>
<!-- (c) 2017 CA Technologies. All Rights Reserved. -->
<!-- Build Date: Tue Dec 08 2020 08:43:41 GMT-0700 (Mountain Standard Time) -->
<script type="text/javascript">
var APP_BUILD_DATE = "Tue Dec 08 2020 08:43:41 GMT-0700 (Mountain Standard Time)";
var ARTIFACT = "";
var BUILDER = "kc683795";
var CHECKSUM = 23527040681;
</script>
<script type="text/javascript" src="/apps/2.1/sdk.js"></script>
<!-- our highcharts (needed so that we can add patterns)
<script type="text/javascript" src="/apps/2.1/lib/analytics/analytics-all.js"></script>
-->
<script type="text/javascript">
Rally.onReady(function() {
Ext.define("RallyCommunity.app.InfoLink",{extend:"Rally.ui.dialog.Dialog",alias:"widget.rallyappinfolink",informationHtml:null,title:"Build Information",defaults:{padding:5,margin:5},closable:!0,draggable:!0,autoShow:!0,width:350,informationalConfig:null,showLog:!1,logger:null,items:[{xtype:"container",itemId:"information"},{xtype:"container",itemId:"button_box"}],initComponent:function(){Ext.id(this);this.title="<span class='icon-help'> </span>"+this.title,this.callParent(arguments)},_generateChecksum:function(a){var b,c=305419896;for(a=a.replace(/var CHECKSUM = .*;/,""),a=a.replace(/var BUILDER = .*;/,""),a=a.replace(/\s/g,""),b=0;b<a.length;b++)c+=a.charCodeAt(b)*b;return c},_checkChecksum:function(a){var b=Ext.create("Deft.Deferred"),c=this;return Ext.Ajax.request({url:document.URL,params:{id:1},success:function(a){if(text=a.responseText,CHECKSUM){var d=c._generateChecksum(text);if(CHECKSUM!==d)return void b.resolve(!1)}b.resolve(!0)}}),b.promise},_addToContainer:function(a){var b=Ext.apply({xtype:"container",height:200,overflowY:!0},this.informationalConfig);a.add(b)},afterRender:function(){var a=Rally.getApp();if(!Ext.isEmpty(this.informationalConfig)){var b=this.down("#information");this._addToContainer(b)}this.showLog&&this.logger&&this.down("#button_box").add({xtype:"rallybutton",text:"Show Log",listeners:{scope:this,click:function(){this.logger.displayLog()}}}),a.isExternal()?this.addDocked({xtype:"container",cls:"build-info",padding:2,dock:"bottom",html:"... Running externally"}):this._checkChecksum(a).then({scope:this,success:function(a){a||this.addDocked({xtype:"container",cls:"build-info",dock:"bottom",padding:2,html:'<span class="icon-warning"> </span>Checksums do not match'})},failure:function(a){console.log("oops:",a)}}),this.callParent(arguments)},beforeRender:function(){if(this.callParent(arguments),this.informationHtml&&this.addDocked({xtype:"component",componentCls:"intro-panel",padding:2,html:this.informationHtml,dock:"bottom"}),this.addDocked({xtype:"container",cls:"build-info",padding:2,dock:"bottom",html:"This app was created for the RallyCommunity."}),APP_BUILD_DATE){var a=Ext.String.format("Built on: {0} <br/>Built by: {1}",APP_BUILD_DATE,BUILDER);ARTIFACT&&(a=a+"<br/>Source artifact: "+ARTIFACT),this.addDocked({xtype:"container",cls:"build-info",padding:2,dock:"top",html:a})}}}),Ext.define("RallyCommunity.app.Logger",{saveForLater:!1,saveLines:100,logArray:[],constructor:function(a){Ext.apply(this,a)},setSaveForLater:function(a){this.saveForLater=a},log:function(a){var b="[ "+Ext.util.Format.date(new Date,"Y-m-d H:i:s.u")+" ]",c=[];c=Ext.Array.push(c,[b]),c=Ext.Array.push(c,Ext.Array.slice(arguments,0)),this.saveForLater&&(this.logArray||(this.logArray=[]),this.logArray.push(c.join(" ")),this.logArray.length>this.saveLines&&this.logArray.shift()),window.console&&console.log.apply(console,c)},getLogText:function(){return this.logArray&&0!==this.logArray.length?this.logArray.join("<br/>"):"-- no log --"},displayLog:function(){var a=this.getLogText();this.popup=Ext.create("Rally.ui.dialog.Dialog",{width:Ext.getBody().getWidth()-20,height:Ext.getBody().getHeight()-20,closable:!0,title:"Log",autoShow:!0,layout:"border",defaults:{layout:"fit",width:"50%",border:!1},items:[{region:"center",xtype:"container",html:a,autoScroll:!0}]})}}),Ext.define("Settings.UserComboBox",{extend:"Rally.ui.combobox.ComboBox",alias:"widget.settingsusercombobox",config:{editable:!0,typeAhead:!0,queryMode:"remote",minChars:0,forceSelection:!0,selectOnFocus:!0,emptyText:"Search...",allowNoEntry:!0,noEntryText:"-- None --",listConfig:{overflowX:"hidden"},storeConfig:{autoLoad:!1,model:"user",pageSize:10,remoteFilter:!0,sorters:[{property:"UserName",direction:"ASC"}]},displayField:"UserName",valueField:"ObjectUUID"},initComponent:function(){this.callParent(arguments),this.getStore()&&this.relayEvents(this.getStore(),["load"],"store")},constructor:function(a){a.value&&Ext.merge(a,{storeConfig:{filters:[{property:"ObjectUUID",operator:"=",value:a.value}]}}),this.callParent(arguments)},beforeQuery:function(a){var b=a.query,c=this.store.filters.getRange(),d=Rally.data.wsapi.Filter.or([{property:"UserName",operator:"contains",value:b},{property:"DisplayName",operator:"contains",value:b},{property:"FirstName",operator:"contains",value:b},{property:"LastName",operator:"contains",value:b},{property:"EmailAddress",operator:"contains",value:b}]);return b?a.query=d.toString():c.length&&(a.query=Rally.data.wsapi.Filter.and(c).toString()),this.callParent(arguments)}}),Ext.define("RallyCommunity.app.notiferApp.Settings",{singleton:!0,validate:function(a,b){_.each(a,function(a,c){b.log("setting ["+c+"]: "+a)});var c=[];return a.objectType||c.push("An object type must be selected in the app settings."),a.messageText||c.push("A message must be specified in app settings"),a.notificationUserField||a.defaultUserUUID||c.push("Please specify a User field or default User to notify."),a.notificationField||c.push("Please specify a field for notifications to be written to."),c},getSettingsFields:function(a,b){var c="5 0 5 0",d=a&&a.query,e=300,f=500,g=b||a.objectFilters||[{property:"Creatable",value:!0}],h=a.allowDefaultToCreator||!1,i=[{name:"objectType",xtype:"rallycombobox",fieldLabel:"Object Type",labelWidth:e,labelAlign:"right",emptyText:"Choose Object Type...",valueField:"TypePath",displayField:"Name",storeConfig:{model:"TypeDefinition",sorters:[{property:"DisplayName"}],fetch:["DisplayName","TypePath"],filters:g,autoLoad:!1,remoteSort:!1,sortOnLoad:!0,remoteFilter:!0},valueField:"TypePath",displayField:"DisplayName",listeners:{change:function(a){a.fireEvent("typeselected",a.getValue(),a.context)},ready:function(a){a.fireEvent("typeselected",a.getValue(),a.context)}},bubbleEvents:["typeselected"],readyEvent:"ready"},{name:"notificationUserField",xtype:"rallyfieldcombobox",fieldLabel:"User Field to Notify",labelWidth:e,width:f,labelAlign:"right",readyEvent:"ready",handlesEvents:{typeselected:function(a,b){var c=Ext.Array.from(a)[0];c?this.refreshWithNewModelType(c,b):(this.store.removeAll(),this.reset())}},_isNotHidden:function(a){return a.attributeDefinition&&"User"===a.attributeDefinition.SchemaType?!0:!1}}];return h===!0?i.push({name:"defaultToCreator",xtype:"rallycheckboxfield",fieldLabel:"Notify Creator if User Field is empty",labelWidth:e,enabled:h}):i.push({name:"defaultUserUUID",xtype:"settingsusercombobox",labelWidth:e,width:f,labelAlign:"right",fieldLabel:"Default User to notify (if User Field is empty)",value:a.defaultUserUUID}),i=i.concat([{name:"notificationField",xtype:"rallyfieldcombobox",fieldLabel:"Notification Field",labelWidth:e,width:f,labelAlign:"right",readyEvent:"ready",handlesEvents:{typeselected:function(a,b){var c=Ext.Array.from(a)[0];c?this.refreshWithNewModelType(c,b):(this.store.removeAll(),this.reset())}},_isNotHidden:function(a){if(a.attributeDefinition){if("TEXT"===a.attributeDefinition.AttributeType&&a.readOnly===!1)return!0;if("Discussion"===a.attributeDefinition.Name)return!0}return!1}},{name:"messageText",xtype:"textarea",labelWidth:e,labelAlign:"right",labelSeparator:"",flex:1,anchor:"100%",fieldLabel:"Nofication Message"},{name:"query",xtype:"textarea",width:"100%",labelWidth:e,labelAlign:"right",labelSeparator:"",fieldLabel:"Query",margin:"2 0 2 0",flex:1,anchor:"100%",plugins:["rallyfieldvalidationui"],emptyText:"Type a Rally Query like ( ObjectID > 0 )...",value:d,validateOnBlur:!0,validateOnChange:!1,validator:function(a){if(!a)return"Query is required.";try{return a&&Rally.data.wsapi.Filter.fromQueryString(a),!0}catch(b){return b.message}},listeners:{validitychange:function(){this.fireEvent("rowvalidate",this)}}},{name:"safetyLimit",xtype:"rallynumberfield",minValue:0,maxValue:a.maxSafetyLimit||1e4,fieldLabel:"Saftey Limit",labelWidth:e},{name:"saveLog",xtype:"rallycheckboxfield",boxLabelAlign:"after",fieldLabel:"",margin:c,boxLabel:'Save Logging<br/><span style="color:#999999;"><i>Save last 100 lines of log for debugging.</i></span>'}])}}),Ext.define("UserNotification.Base",{mixins:{observable:"Ext.util.Observable"},config:{record:null,notificationColor:"red",messageText:null,defaultNotificationUserUUID:null,notificationUserField:null,notificationField:null,defaultToCreator:!1,createdByField:"CreatedBy",identifierField:null},constructor:function(a){this.mixins.observable.constructor.call(this,a),Ext.apply(this,a)},send:function(){this.record||this.fireEvent("notifyerror","No record provided."),this.fireEvent("notifyerror","Base class implemented, no message sent.")},getRecordIdentifier:function(){return this.record.get("FormattedID")||this.record.get("Name")||this.record.get("_ref")},_buildNotificationText:function(){var a=this._getUserNotificationFieldUUID(this.record);if(!a)return this.fireEvent("notifyerror","No user found to mention for "+this.getRecordIdentifier()),null;if(!this.messageText)return this.fireEvent("notifyerror","No message text specified for "+this.getRecordIdentifier()),null;var b='<p><span class="mention" contenteditable="false" data-mention="{0}" style="color:{2}">{1}</span></p>';return Ext.String.format(b,a,this.messageText,this.notificationColor)},_getUserNotificationFieldUUID:function(){var a=this.notificationUserField,b=this.defaultToCreator;if(!a&&!b&&!this.defaultNotificationUserUUID)return null;var c=this.record&&this.record.get(a)&&this.record.get(a)._refObjectUUID||null;return!c&&b&&(c=this.record&&this.record.get(this.createdByField)._refObjectUUID||null),c||this.defaultNotificationUserUUID}}),Ext.define("UserNotification.TextField",{extend:"UserNotification.Base",send:function(){if(!this.record)return void this.fireEvent("notifyerror","No record provided.");var a=this.record.get(this.notificationField)||"",b=this._buildNotificationText();if(null===b)return void this.fireEvent("notifyerror","No notification text provided.");b=Ext.String.format("{0} <br/>{1} {2}",a,Rally.util.DateTime.formatWithDefaultDateTime(new Date),b);var c=this.getRecordIdentifier();this.record.set(this.notificationField,b),this.record.save({callback:function(a,b){b.wasSuccessful()?this.fireEvent("notifysuccess",c):this.fireEvent("notifyerror",c)},scope:this})}}),Ext.define("UserNotification.Discussion",{extend:"UserNotification.Base",send:function(){if(!this.record)return void this.fireEvent("notifyerror","No record provided.");var a=this.record.getCollection("Discussion"),b=this._buildNotificationText();if(null!==b){var c=this.getRecordIdentifier();a.load({callback:function(){a.add({Text:b}),a.sync({success:function(){this.fireEvent("notifysuccess",c)},failure:function(){this.fireEvent("notifyerror","Failed to save mention for "+c)},scope:this})},scope:this})}}}),Ext.define("RallyCommunity.app.ViolationContainer",{extend:"Ext.container.Container",alias:"widget.violationscontainer",config:{record:null,notificationColor:"red",messageText:null,defaultNotificationUserUUID:null,notificationUserField:null,notificationField:null,defaultToCreator:!1,createdByField:"CreatedBy",identifierField:null,settings:{},context:null},constructor:function(a){this.callParent([a]),this.addEvents("loadingstart","loadingerror","loadingcomplete"),this.build()},build:function(){this.update("No violations have been loaded becuase there is no logic. Please extend the Violation container class to include a build method for the display and a getRecords method for the notification.")},getRecords:function(){var a=Ext.create("Deft.Deferred");return a.resolve([]),a}}),Ext.define("RallyCommunity.app.NotifierApp",{extend:"Rally.app.App",componentCls:"app",logger:new RallyCommunity.app.Logger,defaults:{margin:10},integrationHeaders:{name:"RallyCommunity.app.NotifierApp"},config:{defaultSettings:{defaultToCreator:!1,defaultUserUUID:null,messageText:"This is a message",notificationColor:"red",notificationField:null,notificationUserField:"Owner",objectType:null,query:"(ObjectID > 0)",referenceProject:null,useDataContext:!0,violationsContainerType:"violationscontainer",useProjectScope:!1,safetyLimit:100,maxSafetyLimit:1e3}},launch:function(){var a=RallyCommunity.app.notiferApp.Settings.validate(this.getSettings(),this.logger);return a&&a.length>0?void this.add({xtype:"container",itemId:"notification_box",html:a.join("<br/>")}):(this.add({xtype:"container",itemId:"notification_box",layout:"vbox"}),this.enableSendNotifications()&&(this.logger.log("send notifications enabled!"),this.down("#notification_box").add({xtype:"rallybutton",text:"Send Notifications",iconCls:"icon-bell",cls:"send-notifications",handler:this._sendNotifications,scope:this})),this.down("#notification_box").add({xtype:"container",itemId:"notification_text",cls:"notification-label",html:Ext.String.format('<p><span style="color:{1};">{0}</span></p>',this.getSetting("messageText"),this.getSetting("notificationColor")),readOnly:!0,width:"75%"}),this.logger.log("ViolationsContainerType = "+this.getViolationsContainerType()),void this.add({xtype:this.getViolationsContainerType(),itemId:"violations",settings:this.getSettings(),context:this.getContext(),listeners:{loadingstart:this.startLoading,loadingcomplete:this.stopLoading,loadingerror:this._notifyError,scope:this}}))},enableSendNotifications:function(){try{for(var a=this.getContext().getPermissions(),b=Rally.util.Ref.getOidFromRef(this.getContext().getWorkspace()._ref),c=0;c<a.userPermissions.length;c++){if("Subscription Admin"===a.userPermissions[c].Role)return this.logger.log("Current user is subscription administrator. Enabling send notifications."),c=a.userPermissions.length,!0;if("Workspace Admin"===a.userPermissions[c].Role){var d=Rally.util.Ref.getOidFromRef(a.userPermissions[c]._ref);if(d===b)return this.logger.log("Current user is workspace administrator for the current workspace. Enabling send notifications."),!0}}}catch(e){this.logger.log("Error checking permissions: "+e)}return!1},startLoading:function(a){a=a||"Loading possible violations...",this.setLoading(a)},stopLoading:function(){this.setLoading(!1)},_sendNotifications:function(){this.success=0,this.failure=0;var a=this.getNotificationType(),b=this.getSetting("messageText"),c=this.getSetting("defaultUserUUID"),d=this.getSetting("notificationUserField"),e=this.getSetting("defaultToCreator"),f=this.getSetting("notificationColor"),g=this.getSetting("notificationField");this.down("#violations").getRecords().then({success:function(h){this.total=h.length;var i=h.length;this.getSafetyLimit()>0&&i>this.getSafetyLimit()&&(i=this.getSafetyLimit(),Rally.ui.notify.Notifier.showWarning({message:"The number of records meeting the notification criteria ["+this.total+"] exceeds the safety limit of "+i+". Only "+i+" records will be updated with notifications."}));for(var j=0;i>j;j++)try{var k=h[j],l=Ext.create(a,{record:k,messageText:b,defaultNotificationUserUUID:c,notificationUserField:d,defaultToCreator:e,notificationColor:f,notificationField:g,listeners:{notifyerror:this._notifyError,notifysuccess:this._notifySuccess,scope:this}});l.send()}catch(m){this.failure++,this._notifyError("Unexpected error sending notification: "+m)}},failure:this._notifyError,scope:this})},getSafetyLimit:function(){return this.getSetting("safetyLimit")},_notifyError:function(a){this.logger.log("notification Error! ",a),this.failure++,Rally.ui.notify.Notifier.showError({message:a}),this.success+this.failure===this.total&&this._notifyComplete()},_notifySuccess:function(a){this.logger.log("notifying ",a),this.success++,this.success+this.failure===this.total&&this._notifyComplete()},_notifyComplete:function(){this.logger.log("Notification updates complete. Success="+this.success+", Failures="+this.failure+", Total="+this.total);var a=Ext.String.format("{0} of {1} notifications made successfully.",this.success,this.total);Rally.ui.notify.Notifier.show({message:a})},getViolationsContainerType:function(){return this.getSetting("violationsContainerType")},getNotificationType:function(){return"Discussion"===this.getSetting("notificationField")?"UserNotification.Discussion":"UserNotification.TextField"},getSettingsFields:function(){return RallyCommunity.app.notiferApp.Settings.getSettingsFields(this.getSettings())},getOptions:function(){var a=[{text:"About...",handler:this._launchInfo,scope:this}];return a},_launchInfo:function(){this.about_dialog&&this.about_dialog.destroy(),this.about_dialog=Ext.create("RallyCommunity.app.InfoLink",{showLog:this.getSetting("saveLog"),logger:this.logger})},isExternal:function(){return"undefined"==typeof this.getAppId()}}),Ext.define("RallyCommunity.app.TimeboxViolationContainer",{extend:"RallyCommunity.app.ViolationContainer",alias:"widget.timeboxviolationcontainer",config:{record:null,notificationColor:"red",messageText:null,defaultNotificationUserUUID:null,notificationUserField:null,notificationField:null,defaultToCreator:!1,createdByField:"CreatedBy",identifierField:null,settings:{},context:null},displayNames:{StartDate:"Start Date",EndDate:"End Date",ReleaseStartDate:"Release Start Date",ReleaseDate:"Release Date"},build:function(){this._loadData()},getRecords:function(){var a=Ext.create("Deft.Deferred");return this.down("rallygrid")?a.resolve(this.down("rallygrid").getStore().getRange()):a.resolve([]),a},_loadData:function(){var a=this.getTimeboxFields(),b=this.getReferenceProject();Ext.create("Rally.data.wsapi.Store",{model:this.getObjectType(),fetch:a,pageSize:2e3,limit:"Infinity",filters:[{property:"Project",value:b},{property:this.getEndDateField(),operator:">",value:"today"}],sorters:[{property:"StartDate",direction:"ASC"}],context:{project:b,projectScopeUp:!1,projectScopeDown:!1}}).load({callback:this._buildSuspectTimeboxSearches,scope:this})},getReferenceProject:function(){return this.context.getProject()._ref},getObjectType:function(){return this.settings.objectType},getTimeboxFields:function(){var a=["Project","Name"];return a.push(this.getStartDateField()),a.push(this.getEndDateField()),a},getStartDateField:function(){return"Release"==this.getObjectType()?"ReleaseStartDate":"StartDate"},getEndDateField:function(){return"Release"==this.getObjectType()?"ReleaseDate":"EndDate"},getStartTime:function(){var a=this.settings.startTime||new Date;return Rally.util.DateTime.toIsoString(a)},_buildSuspectTimeboxSearches:function(a,b,c){var d=[],e=this.getStartDateField(),f=this.getEndDateField(),g=this.getStartTime();if(!a||0===a.length)return void this._displaySuspects(null);for(var h=0;h<a.length;h++){var i=a[h],j=Rally.data.wsapi.Filter.or([{property:"Name",operator:"!=",value:i.get("Name")},{property:e,operator:"!=",value:i.get(e)},{property:f,operator:"!=",value:i.get(f)}]),k=Rally.data.wsapi.Filter.and([{property:f,operator:"<=",value:i.get(f)},{property:f,operator:">",value:g}]),l=k.and(j);g=i.get(f),d.push(this._loadSuspectTimeboxes(l))}var m=[{property:this.getEndDateField(),operator:">",value:g}];d.push(this._loadSuspectTimeboxes(m)),this.fireEvent("loadingstart"),Deft.Promise.all(d).then({success:this._displaySuspects,failure:this._showError,scope:this}).always(function(){this.fireEvent("loadingcomplete")},this)},_showError:function(a){this.fireEvent("loadingerror",a)},_displaySuspects:function(a){var b=[],c="No misaligned timeboxes found in the current scope.";null===a?c="No reference Timeboxes were found in the current Project for the query and timerange with an EndDate > today.<br/>If there are no reference timeboxes to compare with in the current Project, no misaligned timeboxes can be identified.":b=_.flatten(a);var d=Ext.create("Rally.data.custom.Store",{data:b,pageSize:b.length}),e=this.getTimeboxFieldColumnCfgs();this.down("rallygrid")&&this.down("rallygrid").removeAll(),this.add({xtype:"rallygrid",showRowActionsColumn:!1,editable:!1,store:d,columnCfgs:e,showPagingToolbar:!1,viewConfig:{emptyText:Rally.ui.EmptyTextFactory.getEmptyTextDiv(c)}})},getTimeboxFieldColumnCfgs:function(){var a=Ext.Array.map(this.getTimeboxFields(),function(a){var b=this.displayNames[a]||a,c={dataIndex:a,text:b,flex:1};return"Project"===a&&(c.renderer=function(a){return a&&a._refObjectName}),c},this);return a},_loadSuspectTimeboxes:function(a){var b=Ext.create("Deft.Deferred"),c=this.getObjectType(),d=this.getTimeboxFields(),e={project:null};return this.settings.useProjectScope===!0&&(e.project=this.context.getProject()._ref,e.projectScopeDown=this.getContext().getProjectScopeDown(),e.projectScopeUp=this.getContext().getProjectScopeUp()),Ext.create("Rally.data.wsapi.Store",{model:c,fetch:d,pageSize:2e3,limit:"Infinity",filters:a,context:{project:null}}).load({callback:function(a,c){c.wasSuccessful()?b.resolve(a):b.reject(c&&c.error&&c.error.errors&&c.error.errors.join(",")||"Error loading suspect timeboxes")},scope:this}),b.promise}}),Ext.define("RallyCommunity.app.TimeboxMisalignmentNotifier",{extend:"RallyCommunity.app.NotifierApp",integrationHeaders:{name:"RallyCommunity.app.TimeboxMisalignmentNotifier"},config:{defaultSettings:{violationsContainerType:"timeboxviolationcontainer",objectFilters:Rally.data.wsapi.Filter.or([{property:"TypePath",value:"Iteration"},{property:"TypePath",value:"Release"}]),allowDefaultToCreator:!1}}});
Rally.launchApp('RallyCommunity.app.TimeboxMisalignmentNotifier', {
name: 'Timebox Misalignment Notifier'
});
});
</script>
<style type="text/css">
.app {
}
.tsinfolink {
position:absolute;
right:0px;
width: 14px;
height: 14px;
border-radius: 7px;
text-align: center;
color: white;
background: #C0C0C0;
border-style: solid;
border-width: 1px;
margin-top: 25px;
margin-right: 5px;
cursor: pointer;
}
.send-notifications {
background-color: #EE1C25;
}
.notification-label {
font-family:NotoSans;
white-space:nowrap;
font-size:13px;
}
.x-grid-view-empty {
font-family:NotoSans;
white-space:nowrap;
font-size:14px!important;
color: #A9A9A9;
}
.app {
}
.tsinfolink {
position:absolute;
right:0px;
width: 14px;
height: 14px;
border-radius: 7px;
text-align: center;
color: white;
background: #C0C0C0;
border-style: solid;
border-width: 1px;
margin-top: 25px;
margin-right: 5px;
cursor: pointer;
}
</style>
</head>
<body></body>
</html>