[SalesForce] How to create a Push Notification service in the Service Console.

How about an idea of having the push notifications in the service console. The idea is to have the notification while working in the Service console.

This idea is useful for creating the call center application where the agents have access to service console only.

The new alert should be notified to the agent without refreshing the screen in the real time. We can take an example of facebook . In case of any change/alert a new notification Is shown to the user in the upper right of the screen. This alerts the current logged in user about the new notification.

Best Answer

Push notifications are the visual indicators which notifies the users for the latest alerts and changes in the system.

Push notifications does not require and refresh in current window/state of the user. The browser polls the system at regular intervals and notifies the user in case of change.

This is useful when the user stay on the current page for long and want to get notified in case of any change from backend systems.

In case of service console provided to call center agents, those are typically the 360 degree view of the customers. Now in case of any alert from the management A visual indicator should be provided on that screen only so that without any refresh of current state/window a call center rep should be aware about latest happening useful to him.

This actually helps the call center agents to satisfy customers in the better way. Say, call center agent while working/ attending a call with the customer is trying to pull the data/info about customer which is again integrated with other systems and meanwhile the service got down. So with notifications available there he will be immediately notified about the same. This will actually helps agent to handle the calls in more better way rather to make the customers wait and then regret for the same. The code for the same will be below

    <apex:page standardController="NewsMessage__c" extensions="Ctrl_AgentNewsAlerts" recordSetVar="msgs" id="msgCtrl" sidebar="false" showHeader="false" standardStylesheets="false">
   <apex:includeScript value="{!$Resource.jQuery1_7_2}"/>
   <script src="//code.jquery.com/ui/1.11.1/jquery-ui.js"></script>
   <link rel="stylesheet" href="//code.jquery.com/ui/1.11.1/themes/smoothness/jquery-ui.css" />
   <apex:includeScript value="{!$Resource.cometd}"/>  
   <apex:includeScript value="{!$Resource.json2}"/>  
   <apex:includeScript value="{!$Resource.jquery_cometd}"/>
   <apex:includeScript value="/js/functions.js" />
   <apex:includeScript value="/soap/ajax/29.0/connection.js"/>
   <apex:includeScript value="/support/console/29.0/integration.js"/>
   <style>
      .firstRow{
           background-color:#BFCFE3;
           margin-bottom:6px;
           float:left;
           width:100%;
           border-collapse: separate;
           border-spacing: 0;
           border:solid 1px;
           border-radius:4px;
           -moz-border-radius:4px;
           -webkit-border-radius: 4px;
       }

       .secondRow{
           background-color:#FFEAD3;
           margin-bottom:6px;
           float:left;
           width:100%;
           border-collapse: separate;
           border-spacing: 0;
           border:solid 1px;
           border-radius:4px;
           -moz-border-radius:4px;
           -webkit-border-radius: 4px;
       }
     </style>
<apex:form id="pgForm" style="background-color:#476B8F; height=500px;">
    <apex:actionFunction action="{!refreshMessages}" name="refreshAlerts" rerender="pgForm"/>
    <apex:actionFunction action="{!filterNewsAlerts}" name="refreshDate" rerender="pgForm">
        <apex:param name="agentDispDt" value="" assignTo="{!agentDisplayDate}" />
    </apex:actionFunction>
    <apex:pageBlock id="pgBlck" tabStyle="NewsMessage__c">
        <apex:outputPanel id="firstDiv" layout="block" rendered="true" style="background-color:#476B8F; height:28px; column-gap:6px;">
            <span style="color:#F3F7FD; font-size:18pt; font-family:Arial; font-style:normal;">Agent News and Alerts</span>
            <apex:commandButton status="fetchStatus" style="color:#25496D; float:right; font-size:10pt; font-family:Arial; font-weight:bold;" image="{!URLFOR($Resource.NewsAlertIcons,'Refresh.png')}" reRender="pgForm" title="Refresh" action="{!refreshMessages}"/>
        </apex:outputPanel>
        <apex:outputPanel id="secDiv" layout="block" rendered="true" style="background-color:#25496D; font-color:white; height:28px; column-gap:6px;">
            <apex:inputHidden id="agentDatePicker" value="{!agentDisplayDate}" />&nbsp;&nbsp;
            <apex:outputText id="displayDate" style="color:#6F8FAB; font-size:11pt; font-family:Arial; font-weight:bold;" value="Showing items for {0, date, MMMM d','  yyyy}" rendered="{!isFilter}" >
                <apex:param value="{!agentFilterDate}" />
            </apex:outputText>&nbsp;&nbsp;
            <apex:commandButton status="fetchStatus" image="{!URLFOR($Resource.NewsAlertIcons,'clear-date-10x10.png')}" reRender="pgForm" title="Clear" action="{!refreshMessages}" rendered="{!isFilter}"/>
        </apex:outputPanel>
        <div id="thirdDiv"  align="right" style="background-color:#476B8F; width:100%; height:25px;">
            <apex:commandButton status="fetchStatus" reRender="pgForm" image="{!URLFOR($Resource.NewsAlertIcons,'back-enabled.png')}" action="{!previous}" rendered="{!hasPrevious}" title="Previous Page"/>
            <apex:commandButton status="fetchStatus" image="{!URLFOR($Resource.NewsAlertIcons,'back-disabled.png')}" action="#" disabled="true" rendered="{!!hasPrevious}"/>
            <apex:commandButton status="fetchStatus" reRender="pgForm" image="{!URLFOR($Resource.NewsAlertIcons,'next-enabled.png')}" action="{!next}" rendered="{!hasNext}" title="Next Page"/>
            <apex:commandButton status="fetchStatus" image="{!URLFOR($Resource.NewsAlertIcons,'next-disabled.png')}" action="#" disabled="true" rendered="{!!hasNext}"/>
            <apex:outputPanel style="color:#4AA02C;font-weight:bold">
                <apex:actionStatus id="fetchStatus" startText="Fetching..." stopText=""/>
            </apex:outputPanel>
         </div>
         <apex:dataTable id="pgBlckTbl" value="{!messageList}" var="msg" bgcolor="#476B8F" width="95%" align="center" rowClasses="firstRow, secondRow" cellpadding="6px">
                <apex:column width="10%"> <apex:image url="{!URLFOR($Resource.NewsAlertIcons,msg.Icon_Uri__c)}"/> <br/> </apex:column>
                <apex:column width="90%"> 
                    <apex:outputText value="{!msg.Alert_Message__c}" style="color:#25496D; font-size:13pt; font-family:Arial; font-weight:bold;" escape="false"/>
                    <apex:outputText value="{!msg.Post_Since__c}"  style="color:#25496D; float:right; font-size:10pt; font-family:Arial; font-weight:bold;" escape="false"/>
                    <br/>
                    <apex:outputText value="{!LEFT(msg.Message_Detail__c, 150)}" style="color:#25496D; font-size:11pt; font-family:Arial; font-style:normal;" escape="false"/>
                    <apex:outputLink onClick="readMore('{!JSENCODE(msg.Alert_Message__c)}', '{!JSENCODE(msg.Message_Detail__c)}'); return false;" rendered="{!IF(LEN(msg.Message_Detail__c) > 150, true, false)}" style="color:#25496D; font-size:10pt; font-family:Arial; font-style:italic;" >  (More...)</apex:outputLink>
                    <br/>
                </apex:column>
         </apex:dataTable>
          <div id="fourthDiv" style="background-color:#476B8F; width:100%; height:100px;"> 
          </div>
    </apex:pageBlock>

<script type="text/javascript">
    (function($)  
    {
       $('[id$=agentDatePicker]').datepicker({
                showOn: "both",
                changeMonth: true,
                changeYear: true,
                numberOfMonths: 1,
                dateFormat:"yy-mm-dd",
                showAnim: "slide",
                buttonImage: "{!URLFOR($Resource.NewsAlertIcons,'pick-date-18x18.png')}",
                buttonImageOnly: true,
                buttonText: "Select date",
                onSelect: function(value, date) { 
                  //alert('The chosen date is ' + value);
                  var agentDispDt = value;
                  refreshDate(agentDispDt);
                }
         });
    })(jQuery);
</script>    
<script type="text/javascript">

         window.onbeforeunload = disconnect();

         var j$ = jQuery.noConflict();
         function readMore(alertMsg, detailText) {
            var options = {};
            var dialog = j$('<div></div>')
               .css({overflow:"auto"})
               .html(detailText)
               .dialog({
                    closeOnEscape: true,
                    modal: true,
                    title: alertMsg,
                    height: 300,
                    width: 400,
                    closeText: "Close"
                  });
         }

        //================Streaming API code to refresh status=================== 
        //var j$ = jQuery.noConflict();             

        jQuery.noConflict();
        (function($)  
        {  
            var liveAgentCheckInterval;
            var buttonName = 'News Alert';
            var originalName = 'Agent News and Alerts';
            var flashInterval;
            var lastShade = '#c0e2ea';
            var newReqs = 0;
            var oldReqs = 0;
            var requests = {};

            function setButtonText(reqs) {
                //alert('Inside setButtonText :: ' + reqs);
                var newText = reqs ? buttonName + ' - 1' : originalName;
                //alert('newText ::' + newText);
                sforce.console.setCustomConsoleComponentButtonText(newText);
            }

            function setButtonBgColor(color) {
                //alert('Inside setButtonBgColor:' + color);
                if (!color) {
                    return;
                }
                sforce.console.setCustomConsoleComponentButtonStyle('background-color: ' + color + ';');
            }

            function stopFlashAlert() {
                //alert('Inside stopFlashAlert 1');
                setButtonBgColor('transparent');
                setButtonText(false);
                clearInterval(flashInterval);
                flashInterval = null;
            }

            sforce.console.onCustomConsoleComponentButtonClicked(function() {
                //alert('Inside compButtonClicked');
                setButtonBgColor('transparent');
                clearInterval(flashInterval);
                flashInterval = null;
                //refresh
                if(newReqs > 0){
                   //alert('refreshing page on button clicked');
                   newReqs = 0;
                   setButtonText(false);
                }
                refreshAlerts();
            });

            function flashAlert(reqs) {
                //alert('Inside flashAlert');
                startFlashing = function() {
                    flashInterval = setInterval(function() {
                        lastShade = lastShade == '#E4F3F7' ? '#149BC8' : '#E4F3F7';
                        setButtonBgColor(lastShade);
                    }, 700);
                }

                if(!flashInterval && reqs) {
                    oldReqs = reqs;
                    //alert('Inside flashAlert 2');
                    sforce.console.isCustomConsoleComponentWindowHidden(function(a) {
                        if (a.hidden) {
                            startFlashing();
                        }
                    });
                }

                if (reqs > 0) {
                    setButtonText(reqs);
                } else {
                    stopFlashAlert();
                }
            }

            $(document).ready(function() { 
                var cometd = $.cometd;

                // Connect to the CometD endpoint  
                cometd.init({  
                    url: window.location.protocol+'//'+window.location.hostname+'/cometd/29.0/',  
                    requestHeaders: { Authorization: 'OAuth {!$Api.Session_ID}'}  
                });    
                // Subscribe to a AgentNewsMessage topic  
                cometd.subscribe('/topic/AgentNewsAlert', function(message)  
                {  
                    //on new Alert Addition Streaming API
                    alert('Streaming API');
                    newReqs++;
                    //alert('Value:'+newReqs);
                    flashAlert(newReqs);
                    refreshAlerts();
                });

            });        

        })(jQuery);

         function disconnect() {
              //  alert('Closing Window');
                $.cometd.disconnect();
            }
       //========================================================================
 </script>
</apex:form>
</apex:page>

/**
 * Name     : Ctrl_AgentNewsAlerts
 * Usage    : Ctrl_AgentNewsAlerts class used for building Agent News and Alerts custom console component. 
 *            By querying data from custom object NewsMessage__c and display
 *            
 *  
 * Created By      : Mukul Goel
 * Modified By     : 
 * Modified Date   : Mar26, 2015
 * 
 */
public class Ctrl_AgentNewsAlerts {
    public List<NewsMessage__c> alertNews;
    public Integer noOfRecords{get; set;}
    public Integer size{get;set;}
    public Date agentFilterDate{get;set;}
    public String agentDisplayDate{get;set;}
    public boolean isFilter {get;set;}

    public list<NewsMessage__c> getMessageList(){
        alertNews = new List<NewsMessage__c>();
        for(NewsMessage__c msg : (list<NewsMessage__c>)stdController.getRecords())
            alertNews.add(msg);
        return alertNews;       
    }

    public Ctrl_AgentNewsAlerts(ApexPages.StandardSetController controller) {
            agentDisplayDate = null;
            agentFilterDate = null;
            isFilter = false;
    }

    public ApexPages.StandardSetController stdController {
        get{
            if(stdController == null){
                size = 3;
                string queryString = 'select Id, Name, Alert_Message__c, Message_Detail__c, Post_Since__c, Icon_Uri__c, Detail_Text__c ' +
                    ' from NewsMessage__c where Active__c = true order by CreatedDate desc';
                stdController = new ApexPages.StandardSetController(Database.getQueryLocator(queryString));
                //stdController.setPageNumber(1);
                stdController.setPageSize(size);
                noOfRecords = stdController.getResultSize();
                isFilter = false;
            }
            return stdController;
        }set;
    }

    public PageReference refreshMessages(){
        stdController = null;
        agentFilterDate = null;
        agentDisplayDate = null;
        isFilter = false;
        alertNews = getMessageList();
        stdController.setPageNumber(1);
        return null;
    }

    /*public pageReference refresh() {
        stdController = null;
        agentFilterDate = null;
        agentDisplayDate = null;
        isFilter = false;
        getMessageList();
        stdController.setPageNumber(1);
        return null;
    }*/

    public Boolean hasNext {
        get {
            return stdController.getHasNext();
        }
        set;
    }
    public Boolean hasPrevious {
        get {
            return stdController.getHasPrevious();
        }
        set;
    }

    public Integer pageNumber {
        get {
            return stdController.getPageNumber();
        }
        set;
    }

    /*public void first() {
        stdController.first();
    }

    public void last() {
        stdController.last();
    }*/

    public void previous() {
        stdController.previous();
    }

    public void next() {
        stdController.next();
    } 

    public PageReference filterNewsAlerts(){
        isFilter = true;
        system.debug( 'agentDisplayDate::' + agentDisplayDate);
        if(agentDisplayDate != null && agentDisplayDate.trim().length() > 0)
            agentFilterDate = Date.valueOf(agentDisplayDate);
        system.debug('agentFilterDate::' + agentFilterDate);
        stdController = null;
        size = 3;
        string queryString = 'select Id, Name, Alert_Message__c, Message_Detail__c, Post_Since__c, Icon_Uri__c, Detail_Text__c ' +
            ' from NewsMessage__c where Active__c = true and DAY_ONLY(CreatedDate) =: agentFilterDate  order by CreatedDate desc';
        stdController = new ApexPages.StandardSetController(Database.getQueryLocator(queryString));
        stdController.setPageNumber(1);
        stdController.setPageSize(size);
        noOfRecords = stdController.getResultSize();
        return null;
    }
}

Static resources can be downloaded from here. In folder there is image for the object structure alos. Please create object accordingly .

https://drive.google.com/folderview?id=0B4gJmOMW7SwwUHpLd0s2dzNCWFE&usp=sharing Before proceeding further we need to create the push topic since we are using the streaming api

Execute the following in developer console.

  PushTopic pushTopic = new PushTopic();
pushTopic.ApiVersion = 23.0;
pushTopic.Name = 'NewAlert';
pushTopic.Description = 'Notify if the Alert created';
pushtopic.Query = 'Select id,name,alert_info_url__c, alert_message__c,icon_uri__c From NewsMessage__c';
insert pushTopic;
System.debug('Created new PushTopic: '+ pushTopic.Id);

Once this setup is complete

GOto Setup --> Customize --> Custom console components.

Create a new component with the above visual force page.

Now go to setup --> Create ---> Apps

Select the console app and add the custom console component in the footer.

Go to console page.

Open the News and alert object in another browser. Create a record for the same. As soon as u create the record go to original window. the component in the footer will start flashing automatically without any refresh .

Thanks for reading. I am happy to help if someone requires any kind of help

Related Topic