Let's get started on our agent UI. Assuming you've followed the conventions so far in this tutorial, the UI we create will be accessible using your web browser at:
http://localhost:8080/agent.cshtml?WorkerSid=WK01234012340123401234
(substitute your Alice's WorkerSid)
We pass the WorkerSid in the URL to avoid implementing complex user management in our demo. In reality, you are likely to store a user's WorkerSid in your database alongside other User attributes.
Now create a CSHTML file that will be rendered when the URL is requested:
1@using System;2@using System.Collections.Generic;3@using Twilio;4@using Twilio.Http;5@using Twilio.Jwt.Taskrouter;67@{8class PolicyUrlUtils9{10const string taskRouterBaseUrl = "https://taskrouter.twilio.com";11const string taskRouterVersion = "v1";1213readonly string _workspaceSid;14readonly string _workerSid;1516public PolicyUrlUtils(string workspaceSid, string workerSid)17{18_workspaceSid = workspaceSid;19_workerSid = workerSid;20}2122public string AllTasks => $"{Workspace}/Tasks/**";2324public string Worker => $"{Workspace}/Workers/{_workerSid}";2526public string AllReservations => $"{Worker}/Reservations/**";2728public string Workspace =>29$"{taskRouterBaseUrl}/{taskRouterVersion}/Workspaces/{_workspaceSid}";3031public string Activities => $"{Workspace}/Activities";3233}3435@{36// put your Twilio API credentials here37const string accountSid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";38const string authToken = "your_auth_token";39const string workspaceSid = "WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";40const string workerSid = "WKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";4142var updateActivityFilter = new Dictionary<string, Policy.FilterRequirement>43{44{ "ActivitySid", Policy.FilterRequirement.Required }45};4647var urls = new PolicyUrlUtils(workspaceSid, workerSid);4849var allowActivityUpdates = new Policy(urls.Worker,50HttpMethod.Post,51postFilter: updateActivityFilter);52var allowTasksUpdate = new Policy(urls.AllTasks, HttpMethod.Post);53var allowReservationUpdate = new Policy(urls.AllReservations, HttpMethod.Post);54var allowWorkerFetches = new Policy(urls.Worker, HttpMethod.Get);55var allowTasksFetches = new Policy(urls.AllTasks, HttpMethod.Get );56var allowReservationFetches = new Policy(urls.AllReservations, HttpMethod.Get);57var allowActivityFetches = new Policy(urls.Activities, HttpMethod.Get);5859var policies = new List<Policy>60{61allowActivityUpdates,62allowTasksUpdate,63allowReservationUpdate,64allowWorkerFetches,65allowTasksFetches,66allowReservationFetches6768};6970var capability = new TaskRouterCapability(71accountSid,72authToken,73workspaceSid,74workerSid,75policies: policies);7677var workerToken = capability.ToJwt();78}79<!DOCTYPE html>80<html>81<head>82<title>Customer Care - Voice Agent Screen</title>83<link rel="stylesheet" href="//media.twiliocdn.com/taskrouter/quickstart/agent.css"/>84<script src="https://sdk.twilio.com/js/taskrouter/v1.21/taskrouter.min.js" integrity="sha384-5fq+0qjayReAreRyHy38VpD3Gr9R2OYIzonwIkoGI4M9dhfKW6RWeRnZjfwSrpN8" crossorigin="anonymous"></script>85<script type="text/javascript">86/* Subscribe to a subset of the available TaskRouter.js events for a worker */87function registerTaskRouterCallbacks() {88worker.on('ready', function(worker) {89agentActivityChanged(worker.activityName);90logger("Successfully registered as: " + worker.friendlyName)91logger("Current activity is: " + worker.activityName);92});9394worker.on('activity.update', function(worker) {95agentActivityChanged(worker.activityName);96logger("Worker activity changed to: " + worker.activityName);97});9899worker.on("reservation.created", function(reservation) {100logger("-----");101logger("You have been reserved to handle a call!");102logger("Call from: " + reservation.task.attributes.from);103logger("Selected language: " + reservation.task.attributes.selected_language);104logger("-----");105});106107worker.on("reservation.accepted", function(reservation) {108logger("Reservation " + reservation.sid + " accepted!");109});110111worker.on("reservation.rejected", function(reservation) {112logger("Reservation " + reservation.sid + " rejected!");113});114115worker.on("reservation.timeout", function(reservation) {116logger("Reservation " + reservation.sid + " timed out!");117});118119worker.on("reservation.canceled", function(reservation) {120logger("Reservation " + reservation.sid + " canceled!");121});122}123124/* Hook up the agent Activity buttons to TaskRouter.js */125126function bindAgentActivityButtons() {127// Fetch the full list of available Activities from TaskRouter. Store each128// ActivitySid against the matching Friendly Name129var activitySids = {};130worker.activities.fetch(function(error, activityList) {131var activities = activityList.data;132var i = activities.length;133while (i--) {134activitySids[activities[i].friendlyName] = activities[i].sid;135}136});137138/* For each button of class 'change-activity' in our Agent UI, look up the139ActivitySid corresponding to the Friendly Name in the button's next-activity140data attribute. Use Worker.js to transition the agent to that ActivitySid141when the button is clicked.*/142var elements = document.getElementsByClassName('change-activity');143var i = elements.length;144while (i--) {145elements[i].onclick = function() {146var nextActivity = this.dataset.nextActivity;147var nextActivitySid = activitySids[nextActivity];148worker.update({"ActivitySid":nextActivitySid});149}150}151}152153/* Update the UI to reflect a change in Activity */154155function agentActivityChanged(activity) {156hideAgentActivities();157showAgentActivity(activity);158}159160function hideAgentActivities() {161var elements = document.getElementsByClassName('agent-activity');162var i = elements.length;163while (i--) {164elements[i].style.display = 'none';165}166}167168function showAgentActivity(activity) {169activity = activity.toLowerCase();170var elements = document.getElementsByClassName(('agent-activity ' + activity));171elements.item(0).style.display = 'block';172}173174/* Other stuff */175176function logger(message) {177var log = document.getElementById('log');178log.value += "\n> " + message;179log.scrollTop = log.scrollHeight;180}181182window.onload = function() {183// Initialize TaskRouter.js on page load using window.workerToken -184// a Twilio Capability token that was set from rendering the template with agents endpoint185logger("Initializing...");186window.worker = new Twilio.TaskRouter.Worker("@workerToken");187188registerTaskRouterCallbacks();189bindAgentActivityButtons();190};191</script>192</head>193<body>194<div class="content">195<section class="agent-activity offline">196<p class="activity">Offline</p>197<button class="change-activity" data-next-activity="Idle">Go Available</button>198</section>199<section class="agent-activity idle">200<p class="activity"><span>Available</span></p>201<button class="change-activity" data-next-activity="Offline">Go Offline</button>202</section>203<section class="agent-activity reserved">204<p class="activity">Reserved</p>205</section>206<section class="agent-activity busy">207<p class="activity">Busy</p>208</section>209<section class="agent-activity wrapup">210<p class="activity">Wrap-Up</p>211<button class="change-activity" data-next-activity="Idle">Go Available</button>212<button class="change-activity" data-next-activity="Offline">Go Offline</button>213</section>214<section class="log">215<textarea id="log" readonly="true"></textarea>216</section>217</div>218</body>219</html>
You'll notice that we included two external files:
And that's it! Compile your Java class and start your server.
Open http://localhost:8080/agent.cshtml?WorkerSid=WK01234012340123401234
in your browser and you should see the screen below. If you make the same phone call as we made in Part 3, you should see Alice's Activity transition on screen as she is reserved and assigned to handle the Task.
If you see "Initializing..." and no progress, make sure that you have included the correct WorkerSid in the "WorkerSid" request parameter of the URL.
For more details, refer to the TaskRouter JavaScript SDK documentation.