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:
_219 @using System;_219 @using System.Collections.Generic;_219 @using Twilio;_219 @using Twilio.Http;_219 @using Twilio.Jwt.Taskrouter;_219_219 @{_219 class PolicyUrlUtils_219 {_219 const string taskRouterBaseUrl = "https://taskrouter.twilio.com";_219 const string taskRouterVersion = "v1";_219_219 readonly string _workspaceSid;_219 readonly string _workerSid;_219_219 public PolicyUrlUtils(string workspaceSid, string workerSid)_219 {_219 _workspaceSid = workspaceSid;_219 _workerSid = workerSid;_219 }_219_219 public string AllTasks => $"{Workspace}/Tasks/**";_219_219 public string Worker => $"{Workspace}/Workers/{_workerSid}";_219_219 public string AllReservations => $"{Worker}/Reservations/**";_219_219 public string Workspace =>_219 $"{taskRouterBaseUrl}/{taskRouterVersion}/Workspaces/{_workspaceSid}";_219_219 public string Activities => $"{Workspace}/Activities";_219_219 }_219_219 @{_219 // put your Twilio API credentials here_219 const string accountSid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";_219 const string authToken = "your_auth_token";_219 const string workspaceSid = "WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";_219 const string workerSid = "WKXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";_219_219 var updateActivityFilter = new Dictionary<string, Policy.FilterRequirement>_219 {_219 { "ActivitySid", Policy.FilterRequirement.Required }_219 };_219_219 var urls = new PolicyUrlUtils(workspaceSid, workerSid);_219_219 var allowActivityUpdates = new Policy(urls.Worker,_219 HttpMethod.Post,_219 postFilter: updateActivityFilter);_219 var allowTasksUpdate = new Policy(urls.AllTasks, HttpMethod.Post);_219 var allowReservationUpdate = new Policy(urls.AllReservations, HttpMethod.Post);_219 var allowWorkerFetches = new Policy(urls.Worker, HttpMethod.Get);_219 var allowTasksFetches = new Policy(urls.AllTasks, HttpMethod.Get );_219 var allowReservationFetches = new Policy(urls.AllReservations, HttpMethod.Get);_219 var allowActivityFetches = new Policy(urls.Activities, HttpMethod.Get);_219_219 var policies = new List<Policy>_219 {_219 allowActivityUpdates,_219 allowTasksUpdate,_219 allowReservationUpdate,_219 allowWorkerFetches,_219 allowTasksFetches,_219 allowReservationFetches_219_219 };_219_219 var capability = new TaskRouterCapability(_219 accountSid,_219 authToken,_219 workspaceSid,_219 workerSid,_219 policies: policies); _219_219 var workerToken = capability.ToJwt();_219 }_219 <!DOCTYPE html>_219 <html>_219 <head>_219 <title>Customer Care - Voice Agent Screen</title>_219 <link rel="stylesheet" href="//media.twiliocdn.com/taskrouter/quickstart/agent.css"/>_219 <script src="https://sdk.twilio.com/js/taskrouter/v1.21/taskrouter.min.js" integrity="sha384-5fq+0qjayReAreRyHy38VpD3Gr9R2OYIzonwIkoGI4M9dhfKW6RWeRnZjfwSrpN8" crossorigin="anonymous"></script>_219 <script type="text/javascript">_219 /* Subscribe to a subset of the available TaskRouter.js events for a worker */_219 function registerTaskRouterCallbacks() {_219 worker.on('ready', function(worker) {_219 agentActivityChanged(worker.activityName);_219 logger("Successfully registered as: " + worker.friendlyName)_219 logger("Current activity is: " + worker.activityName);_219 });_219_219 worker.on('activity.update', function(worker) {_219 agentActivityChanged(worker.activityName);_219 logger("Worker activity changed to: " + worker.activityName);_219 });_219_219 worker.on("reservation.created", function(reservation) {_219 logger("-----");_219 logger("You have been reserved to handle a call!");_219 logger("Call from: " + reservation.task.attributes.from);_219 logger("Selected language: " + reservation.task.attributes.selected_language);_219 logger("-----");_219 });_219_219 worker.on("reservation.accepted", function(reservation) {_219 logger("Reservation " + reservation.sid + " accepted!");_219 });_219_219 worker.on("reservation.rejected", function(reservation) {_219 logger("Reservation " + reservation.sid + " rejected!");_219 });_219_219 worker.on("reservation.timeout", function(reservation) {_219 logger("Reservation " + reservation.sid + " timed out!");_219 });_219_219 worker.on("reservation.canceled", function(reservation) {_219 logger("Reservation " + reservation.sid + " canceled!");_219 });_219 }_219_219 /* Hook up the agent Activity buttons to TaskRouter.js */_219_219 function bindAgentActivityButtons() {_219 // Fetch the full list of available Activities from TaskRouter. Store each_219 // ActivitySid against the matching Friendly Name_219 var activitySids = {};_219 worker.activities.fetch(function(error, activityList) {_219 var activities = activityList.data;_219 var i = activities.length;_219 while (i--) {_219 activitySids[activities[i].friendlyName] = activities[i].sid;_219 }_219 });_219_219 /* For each button of class 'change-activity' in our Agent UI, look up the_219 ActivitySid corresponding to the Friendly Name in the button's next-activity_219 data attribute. Use Worker.js to transition the agent to that ActivitySid_219 when the button is clicked.*/_219 var elements = document.getElementsByClassName('change-activity');_219 var i = elements.length;_219 while (i--) {_219 elements[i].onclick = function() {_219 var nextActivity = this.dataset.nextActivity;_219 var nextActivitySid = activitySids[nextActivity];_219 worker.update({"ActivitySid":nextActivitySid});_219 }_219 }_219 }_219_219 /* Update the UI to reflect a change in Activity */_219_219 function agentActivityChanged(activity) {_219 hideAgentActivities();_219 showAgentActivity(activity);_219 }_219_219 function hideAgentActivities() {_219 var elements = document.getElementsByClassName('agent-activity');_219 var i = elements.length;_219 while (i--) {_219 elements[i].style.display = 'none';_219 }_219 }_219_219 function showAgentActivity(activity) {_219 activity = activity.toLowerCase();_219 var elements = document.getElementsByClassName(('agent-activity ' + activity));_219 elements.item(0).style.display = 'block';_219 }_219_219 /* Other stuff */_219_219 function logger(message) {_219 var log = document.getElementById('log');_219 log.value += "\n> " + message;_219 log.scrollTop = log.scrollHeight;_219 }_219_219 window.onload = function() {_219 // Initialize TaskRouter.js on page load using window.workerToken -_219 // a Twilio Capability token that was set from rendering the template with agents endpoint_219 logger("Initializing...");_219 window.worker = new Twilio.TaskRouter.Worker("@workerToken");_219_219 registerTaskRouterCallbacks();_219 bindAgentActivityButtons();_219 };_219 </script>_219 </head>_219 <body>_219 <div class="content">_219 <section class="agent-activity offline">_219 <p class="activity">Offline</p>_219 <button class="change-activity" data-next-activity="Idle">Go Available</button>_219 </section>_219 <section class="agent-activity idle">_219 <p class="activity"><span>Available</span></p>_219 <button class="change-activity" data-next-activity="Offline">Go Offline</button>_219 </section>_219 <section class="agent-activity reserved">_219 <p class="activity">Reserved</p>_219 </section>_219 <section class="agent-activity busy">_219 <p class="activity">Busy</p>_219 </section>_219 <section class="agent-activity wrapup">_219 <p class="activity">Wrap-Up</p>_219 <button class="change-activity" data-next-activity="Idle">Go Available</button>_219 <button class="change-activity" data-next-activity="Offline">Go Offline</button>_219 </section>_219 <section class="log">_219 <textarea id="log" readonly="true"></textarea>_219 </section>_219 </div>_219 </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.