Forums/Community/Community tips & tricks

Agent response time widget. (Zendesk Classic)

Skip Moore
posted this on November 22, 2010 08:11

The code for the widget is maintained on Github

 

This widget lets you log the difference between the creation and the agents first comment in seconds to a custom number field. Create a custom number field and take note of the field ID
tech_assistant___Edit_numeric_field.png 

Then create a custom widget. This will create a sidebar widget which will display the time in Hours and Minutes of the agents first response. You can edit this and place any kind of text in the widget or value. The ticket field you create will not be editable but still visible to the agents. 

There are several spots where the ID code has to be placed. 

realagenttime.js_at_master_from_skipjac_s_zendesk-widgets_-_GitHub.png

realagenttime.js_at_master_from_skipjac_s_zendesk-widgets_-_GitHub-1.png

This is what it looks like with no Agent response 

_722__Creating_a_new_ticket_for_demo_-1.png

 

With a agent response 

_722__Creating_a_new_ticket_for_demo_-4.png

 

the code 

/*this widget figures out the time from creation of the ticket to the 
first comment a agent makes public or private. The only thing you have to 
edit is the input#ticket_fields_250972 for the Id of the field your system.
Right now is will save the difference in seconds but I have placed other 
variables that give hour dotted time and hours and minutes. The 
sidebar will display the hours and mintues. Just copy the code and place
it in a custom Zendesk widget.  
*/

<div id="realTime">
</div>
<script type="javascript">

	pausecomp = function(millis) 
	{
		var date = new Date();
		var curDate = null;

		do { curDate = new Date(); } 
		while(curDate-date < millis);
	} 

	//thank you http://webcloud.se/log/JavaScript-and-ISO-8601/
	Date.prototype.setISO8601 = function (string) {
		var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
		"(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\:([0-9]+))?)?" +
		"(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
		var d = string.match(new RegExp(regexp));

		var offset = 7;
		var date = new Date(d[1], 0, 1);

		if (d[3]) { date.setMonth(d[3] - 1); }
		if (d[5]) { date.setDate(d[5]); }
		if (d[7]) { date.setHours(d[7]); }
		if (d[8]) { date.setMinutes(d[8]); }
		if (d[10]) { date.setSeconds(d[10]); }
		if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
		if (d[14]) {
			offset = (Number(d[16]) * 60) + Number(d[17]);
			offset *= ((d[15] == '-') ? 1 : -1);
		}

		offset -= date.getTimezoneOffset();
		time = (Number(date) + (offset * 60 * 1000));
		this.setTime(Number(time));
	}   



$j(document).ready(function() {
    $j('input#ticket_fields_250972').attr("disabled", true);
  //Check to see if there isn't already a value
  if(!$j('input#ticket_fields_250972').val()) {
	
	//setting the DOM Object to attach the queue to
	var para = $j("select#ticket_assignee_id");
        var roleID = new Array();
	var count = 0;
	var commentAuthor = new Array();
	var ticketCreated = new Array();
	
        //beginning of queue
	para.queue("testQueue", function( next ){$j.get('/tickets/{{ticket.id}}.xml', 
		function(ticketCreateAt) {
			$j(ticketCreateAt).find('comment').each(function() {
				ticketCreated[count] = $j(this).find('created-at').text();
				commentAuthor[count] = $j(this).find('author-id').text();
				var timeUNIX = new Date();
				timeUNIX.setISO8601(ticketCreated[count]);
				ticketCreated[count] = Date.parse(timeUNIX)/1000;
				//displays in the sidewidget it can be removed.
				count++;
			});
			
		});
		next();  
	});
	


	para.delay( 500, "testQueue" );

	para.queue("testQueue", function( next ) {
		var i = 0;
		while( i < commentAuthor.length){
                        //pause to wait results 
			pausecomp(500);
			$j.get('/users/'+commentAuthor[i]+'.xml', function(findRole) {
				
				$j(findRole).find('user').each(function(){
					roleID.push($j(this).find('roles').text());
					
				});
			});
			
			i++;
		}
		next();
	});

	para.delay( 500, "testQueue" );

	para.queue("testQueue", function( next ) {
		var c = 1;
		
        do 
           { 

             if(roleID[c] != 0){agentUpdate = 'true'; break;}
            else {
              agentUpdate = 'false';
              c++;
             }
         } while (c <= roleID.length);

       if(agentUpdate === 'true'){
            //difference in seconds 
            time1 = (ticketCreated[c]- ticketCreated[0]);
            //difference in hour dot notation for example 2.3 hours
            diffTime = (ticketCreated[c]- ticketCreated[0])/3600;
            //minuteTime figures out the minutes from diffTime
            minuteTime = diffTime+''
            minuteTime =  minuteTime.split('.');
            minuteTime[1] = '.'+minuteTime[1];
            minuteTime[1] = parseFloat(minuteTime[1])*60;

             if(diffTime > 0) {
                 $j('#realTime').append('Hours: '+diffTime.toFixed(0)+' minutues '+ minuteTime[1].round());

                 //places the time in seconds in the text box on the form
                  $j('input#ticket_fields_250972').val(parseInt(time1));
              }else { $j('#realTime').append("No Agent has responded yet");}


         }
		
		next();
	});


	para.dequeue( "testQueue" );
  }
  else {
    diffTime = ($j('input#ticket_fields_250972').val())/3600;
    minuteTime = diffTime+''
    minuteTime =  minuteTime.split('.');
    minuteTime[1] = '.'+minuteTime[1];
    minuteTime[1] = parseFloat(minuteTime[1])*60;
    //to fixed found here http://www.electrictoolbox.com/javascript-fixed-digits-after-decimal-places/
    //places nice time in hours and minutes in the sidebar

    $j('#realTime').append('Hours: '+diffTime.toFixed(0)+' minutues '+ minuteTime[1].round());
  }
});         


</script>
 

Comments

User photo
Gidi Delayahu

Thanks! That's a good Idea, and I will implement it shortly...

 

Would it be possible to to have a widget that tells you how long you have to the expiry of the SLA?

 

Gidi

December 09, 2010 01:51
User photo
Zachary Barvitz
elemental
Does this widget still work? I followed the directions as listed however nothing is being reported within the ticket or on the widget itself. Zach
August 24, 2011 14:10
User photo
Luke Healey
integrityservices

I'm assuming that recent changes to Zendesk code has broken this widget - pity, as it would be very useful - any comments?

September 26, 2011 16:19
User photo
Skip Moore
Zendesk

@Luke what are the problems you are seeing? 

September 27, 2011 09:21
User photo
Luke Healey
integrityservices

Hi Skip –

 

Widget is shown on page, but no time shown either in ticket field (custom numerical) or widget.

I have checked that I have updated the widget code with my Custom field ID: 20356343

$j(document).ready(function() {
$j('input#ticket_fields_20356343').attr("disabled", true);
//Check to see if there isn't already a value
if(!$j('input#ticket_fields_20356343').val()) {

.....

//places the time in seconds in the text box on the form
$j('input#ticket_fields_20356343').val(parseInt(time1));
}else { $j('#realTime').append("No Agent has responded yet");}


}

next();
});


para.dequeue( "testQueue" );
}
else {
diffTime = ($j('input#ticket_fields_20356343').val())/3600;



September 27, 2011 12:26
User photo
Zachary Barvitz
elemental

I get the same thing as Luke, widget on the screen but nothing on the widget or in the field.

September 27, 2011 13:17
User photo
Jonathan Lane

Hey @Skip,

I'm having the same problem as Luke and Zachary -- the box shows up in the sidebar, but no numbers are being populated.

 

Any ideas? 

October 14, 2011 15:11
User photo
Skip Moore
Zendesk

@Jonathan I am working on a rewrite of the widget now. No ETA yet but I will post the new code when I get done. 

October 14, 2011 15:18
User photo
Jonathan Lane

Awesome Skip, thanks a bunch!

October 14, 2011 15:23
User photo
Zachary Barvitz
elemental

Skip that is much appreciated.

October 14, 2011 15:24
User photo
Skip Moore
Zendesk

Hi Everybody I have figured out what was happening. I had set the code to run when the group drop down was loaded. If you didn't have a group then the code didn't run I have updated the code to run when the agent drop down is loaded. Every ticket needs a assignee so everybody should have that field on the page

@Luke I have updated your code and it's working now

Cheers
Skip 

October 14, 2011 21:26
User photo
Luke Healey
integrityservices

Awesome Dude!!  - Many thanks :) 

October 14, 2011 21:32
User photo
Zachary Barvitz
elemental

Thanks Skip, I have it working now.  Much appreciated.

October 19, 2011 14:02
User photo
J.R. Miller
einstruction

Thanks for a fantastic tool!

January 27, 2012 13:37
User photo
CJ

Hi Skip,

I tried your code by adding a widget using Global Javascript but I can't see any widget on my ticket view. Any help will be highly appreciated!

 

Thanks!

May 01, 2012 22:51
User photo
Skip Moore
Zendesk

@Christopher The code is designed to be used in a custom widget not a global widget. Just copy it to a custom widget and then on the ticket view page click edit widgets and add it from the list. 

May 02, 2012 08:46
User photo
CJ

@Skip

Thanks! I finally manage to make it work. I just have a question when extracting a report thru csv. Is it possible to include the seconds in csv? Because I tried downloading the csv file, under my custom number field, the only details I have is (-) not the value in seconds. Thanks again skip!

May 02, 2012 18:48
User photo
Skip Moore
Zendesk

@CJ did you set up the ticket field id correctly in the code? 

May 02, 2012 19:09
User photo
CJ

@Skip

Yes, I can see the time(seconds) on my custom number field and so as to the widget that I created. The problem now is the elapsed time shown is not the complete elapsed time for the ticket conversation(requestor - client conversation). I notice that the elapsed time shown is from the time stamp of New ticket upto the time where an agent replied to that ticket.

Kindly see attach file for your reference.

 

Thanks Skip!

CJ

May 03, 2012 01:34
User photo
Skip Moore
Zendesk

@CJ that is how the widget is designed, just to capture the 1st response time of the agent. You should get the total conversation time in your GoodData reports if you export the full metrics. I wrote this before the full metrics upgrade. 

Skip

May 03, 2012 10:53
User photo
Ryan
skyslope

Hi,

I cannot seem to get this to work. It is just blank. I made sure to check the ticket# being correct. The only thing I can think of is that we have no agents. All of us are admins. Could that be causing the issue? Thanks.

May 24, 2012 18:52
User photo
CJ

Hi Ryan,

Admins are also considered as agents if i'm not mistaken. Also, in which widget you are putting your code? It should be in custom widget not global widget. Hope this helps.

CJ

May 24, 2012 19:20
User photo
CJ

Hi guys,

I would like to share this manual timer widget that I created based on diff. source codes that I collected.

The widget is just a normal timer where you need to click the start upon opening the ticket then click stop after answering the ticket. The time(in seconds) then will be recorded in your Custom Number Field (that would be for example "Response Time"). 

*Just a reminder, if the custom number field is "blank" then you have to click "Set Time" button to record the time in your number field. After that, you can just start and stop the time every time you reply to the ticket.

 

Here is the code(change the "20980941" to your custom number field ID):

<html>
<head>
<script type="text/javascript">
var c=0;
var t;
var timer_is_on=0;
var a=$j('input#ticket_fields_20980941').val();

reSet = function()
{
c=0;
document.getElementById('txt').value=c;
}

timedCount = function()
{
document.getElementById('txt').value=c;
c=c+1;
t=setTimeout("timedCount()",1000);
$j('input#ticket_fields_20980941').val(parseInt(c-1)+parseInt(a));

}

setTime = function()
{
$j('input#ticket_fields_20980941').val(parseInt(c-1));
}

doTimer = function()
{
if (!timer_is_on)
{
timer_is_on=1;
timedCount();
}
}

stopCount = function()
{
clearTimeout(t);
timer_is_on=0;
}

</script>
</head>

<body>
<form>
<input type="text" id="txt" />
<input type="button" value=" Start " onclick="doTimer()" />
<input type="button" value=" Stop " onclick="stopCount()" />
<input type="button" value=" Reset " onclick="reSet()" />
<input type="button" value=" Set Time " onclick="setTime()" />
</form>
</body>
</html>

 

If anyone here can modify and make it run upon loading the ticket page, then that would be awesome :) I already tried using onLoad to automatically start the timer but I can't make it work .

Attach here is the screen shot of the widget.

*Key Functions
 •Start - starts the timer to count in seconds 
•Stop - stops the timer
•Reset - reset the current time in your widget (not in custom number field)
•Set Time - you should only use this during the 1st reply of your ticket to save the time because the value in Custom Number Field is "NaN".

May 24, 2012 22:31
User photo
Ryan
skyslope

Hi CJ,

Thanks for the reply. I did check to make sure that it was a custom wigit and not a global wigit, after reading through the comments here. Any other advice or anything I can provide that may help?

May 25, 2012 09:03
User photo
CJ

Hi Ryan, 

Did you change the custom number field ID in the code given by skip?If it's possible can you provide the code and let me and other's check it to know the cause of your problem. 

 

Regards,

CJ

May 27, 2012 18:04
User photo
Ryan
skyslope

I believe I changed each of the required fields. Here is the code below, thanks.

 

<div id="realTime">
</div>
<script type="javascript">

pausecomp = function(millis)
{
var date = new Date();
var curDate = null;

do { curDate = new Date(); }
while(curDate-date < millis);
}

//thank you http://webcloud.se/log/JavaScript-and-ISO-8601/
Date.prototype.setISO8601 = function (string) {
var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
"(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\:([0-9]+))?)?" +
"(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
var d = string.match(new RegExp(regexp));

var offset = 7;
var date = new Date(d[1], 0, 1);

if (d[3]) { date.setMonth(d[3] - 1); }
if (d[5]) { date.setDate(d[5]); }
if (d[7]) { date.setHours(d[7]); }
if (d[8]) { date.setMinutes(d[8]); }
if (d[10]) { date.setSeconds(d[10]); }
if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
if (d[14]) {
offset = (Number(d[16]) * 60) + Number(d[17]);
offset *= ((d[15] == '-') ? 1 : -1);
}

offset -= date.getTimezoneOffset();
time = (Number(date) + (offset * 60 * 1000));
this.setTime(Number(time));
}

 

$j(document).ready(function() {
$j('input#ticket_fields_21082437

').attr("disabled", true);
//Check to see if there isn't already a value
if(!$j('input#ticket_fields_21082437

').val()) {

//setting the DOM Object to attach the queue to
var para = $j("select#ticket_assignee_id");
var roleID = new Array();
var count = 0;
var commentAuthor = new Array();
var ticketCreated = new Array();

//beginning of queue
para.queue("testQueue", function( next ){$j.get('/tickets/{{ticket.id}}.xml',
function(ticketCreateAt) {
$j(ticketCreateAt).find('comment').each(function() {
ticketCreated[count] = $j(this).find('created-at').text();
commentAuthor[count] = $j(this).find('author-id').text();
var timeUNIX = new Date();
timeUNIX.setISO8601(ticketCreated[count]);
ticketCreated[count] = Date.parse(timeUNIX)/1000;
//displays in the sidewidget it can be removed.
count++;
});

});
next();
});

 

para.delay( 500, "testQueue" );

para.queue("testQueue", function( next ) {
var i = 0;
while( i < commentAuthor.length){
//pause to wait results
pausecomp(500);
$j.get('/users/'+commentAuthor[i]+'.xml', function(findRole) {

$j(findRole).find('user').each(function(){
roleID.push($j(this).find('roles').text());

});
});

i++;
}
next();
});

para.delay( 500, "testQueue" );

para.queue("testQueue", function( next ) {
var c = 1;

do
{

if(roleID[c] != 0){agentUpdate = 'true'; break;}
else {
agentUpdate = 'false';
c++;
}
} while (c <= roleID.length);

if(agentUpdate === 'true'){
//difference in seconds
time1 = (ticketCreated[c]- ticketCreated[0]);
//difference in hour dot notation for example 2.3 hours
diffTime = (ticketCreated[c]- ticketCreated[0])/3600;
//minuteTime figures out the minutes from diffTime
minuteTime = diffTime+''
minuteTime = minuteTime.split('.');
minuteTime[1] = '.'+minuteTime[1];
minuteTime[1] = parseFloat(minuteTime[1])*60;

if(diffTime > 0) {
$j('#realTime').append('Hours: '+diffTime.toFixed(0)+' minutues '+ minuteTime[1].round());

//places the time in seconds in the text box on the form
$j('input#ticket_fields_21082437

').val(parseInt(time1));
}else { $j('#realTime').append("No Agent has responded yet");}


}

next();
});


para.dequeue( "testQueue" );
}
else {
diffTime = ($j('input#ticket_fields_21082437

').val())/3600;
minuteTime = diffTime+''
minuteTime = minuteTime.split('.');
minuteTime[1] = '.'+minuteTime[1];
minuteTime[1] = parseFloat(minuteTime[1])*60;
//to fixed found here http://www.electrictoolbox.com/javascript-fixed-digits-after-decima...
//places nice time in hours and minutes in the sidebar

$j('#realTime').append('Hours: '+diffTime.toFixed(0)+' minutues '+ minuteTime[1].round());
}
});


</script>

May 29, 2012 08:51
User photo
CJ

Hi Ryan,

I'm not sure on what I did but please try the code on the attach txt file. 
Just copy and paste it on your custom widget.

Regards,
CJ

May 30, 2012 00:26
User photo
Marianne
mindsnacks

Hi Skip,

Thanks for the great widget!  I've successfully installed the widget, and it's accurately displaying time to first response.  How can I get a monthly report of this metric?  I'd like to know stats such as "Tickets responded to in <2hrs; <8hrs; <24hrs."  We are using a Regular account (not Plus+ or Enterprise).  Am I able to generate a report for this? The only fields that show up when I create a new report are the ones we have dropdowns for; a numeric field doesn't show as an option for the report.  Any help would be much appreciated.

Many thanks,

Marianne

June 04, 2012 12:04
User photo
Ryan
skyslope

Thank you CJ

June 05, 2012 07:58
User photo
Oscar Tobar
Zendesk

Hi Marianne! I will be opening a ticket to answer your question. You'll receive a notification in your email.

June 05, 2012 10:15
User photo
Mostafa
tophatmonocle

Thanks for the great tip. I tried implementing this and it seems to work for the widget on the right hand side, but it doesn't display anything in the numeric field. Is there anything I'm missing?

Oscar: would it be possible to get the same information you sent Marianne? I'm interested in doing the same thing.

June 14, 2012 16:49
User photo
Jennifer Rowe
Zendesk

Hi Mostafa,

Oscar determined that the type of reporting Marianne wanted to do (view a report by "First reply time in hours" ) is not available in the Regular plan, but is in Plus and Enterprise plans. It sounds like your problem might be different...? If you'd like to have someone help you directly, send a ticket to support@zendesk.com. Good luck!

June 15, 2012 09:26
User photo
Mostafa
tophatmonocle

Hi Jennifer,

Thanks for your reply. We are considering switching to a plus plan soon, do you have some sort of free trial that allows us to test it out for a few days?

I will create a ticket regarding the numeric field not filling up.

June 15, 2012 10:25
User photo
Jennifer Rowe
Zendesk

Hi Mostafa, I will have someone from our sales team reach out to you about your plan and potential upgrade! Thanks!

June 15, 2012 11:01
User photo
cedric dana

This is awesome! Thanks, is there a way I can set it to only count during certain hours. Is there a way I can have an option to have it set to the last response time of the agent?

July 26, 2012 14:10
User photo
Lisa Moody
jewelcode

Hi Skip, Thank you for this widget!  I love it.  Is there a way that I can alter it and use it on solved/closed tickets to see what the historical first response was?  It only seems to work on already open and I'm not a developer so I'm not sure how to change the code to work on solved/closed tickets too so that I can look back and see the number easily on things that are done in the past.  Thanks, Lisa

September 26, 2012 09:06
User photo
Skip Moore
Zendesk

Hi Lisa, Closed tickets can't be edited so unfortunately the widget can't work on them.  

October 04, 2012 22:54
User photo
Charlie
Groupon APAC

Hi Skip, is it possible to alter the code to calculate only public comment reply time?

April 15, 2013 20:37
User photo
Skip Moore
Zendesk

@Charlie it should be possible to modify the code to do a calculate on public comment reply time. That should already be in  your GoodData reports though as Requester Wait time.

April 15, 2013 21:11
User photo
Charlie
Groupon APAC

Hey Skip, thanks for the reply, yes of course everything is in GD but I like that any agent working in ZD can be aware of their customer reply times on a ticket by ticket basis. If it's easy can you tell me which part of the code I need to change to make it happen?

April 15, 2013 21:18
User photo
Skip Moore
Zendesk

@Charlie, This is some really old code but if I had to I would try something here by adding a if statement to see if the comment was public

$j(ticketCreateAt).find('comment').each(function() { 
ticketCreated[count] = $j(this).find('created-at').text();
commentAuthor[count] = $j(this).find('author-id').text();
var timeUNIX = new Date();
timeUNIX.setISO8601(ticketCreated[count]);
ticketCreated[count] = Date.parse(timeUNIX)/1000; //displays in the sidewidget it can be removed. count++;
});
April 15, 2013 21:33
User photo
CJ

Hi Skip,

May I ask for your assistance if possible to convert a simple timer widget that I created (HTML Widget) so that we can use it to Zendesk Lotus as an App?


Here's the link for the widget that I created : https://support.zendesk.com/entries/22495046-Timer-Widget


Thank you,
CJ

July 03, 2013 22:56