Lab 2 - Admin Actions
Story
As a contact center administrator, I need to log out agents which have not logged out after their shift, so that the contact center reporting statistics are accurate.
Requirements
- Need a list of logged in agents which includes:
- Agent name
- Team name
- Login time
- Current Status
- Last Activity time
- Need to be able to log out an agent
Data and Actions
Log into the Agent Desktop (so that you have data to query against)
Launch the Agent Desktop
Login:
admin login
Password:password
Team:team
Use the Search API GraphQL Workbench to create the query for the Search API
Open the Search API GraphQL Workbench
Use the Authorization Tool (Tools > Authorization)
Login:
admin login
Password:password Set the URL:
https://api.wxcc-us1.cisco.com/search Open the Docs Panel Click Query to see which fields are available
Which Fields are we going to use to find information about agents?
agentSession
Click on the blue text of the name of the field you want to use
You can now see the Arguments and Fields availableHover over the name of the fields you selected in the previous step at the top of the frame and the ADD QUERY button will appear, click it
You now have a query template in the first pane of Altair
In the Fields section of the query (agentSessions section):
Delete the lines for intervalStartTime(sort: asc) and aggregation as we are not using them in this query.
In the Arguments section of the query (top section):
Delete the lines for aggregation, aggregations, and aggregationInterval as we are not using aggregations in this query.
Based on the requirements listed above in the story, what fields do you need to return in your query?
- Agent name = agentName
- Team name = teamName
- Login time = startTime
- Current Status = channelInfo => currentState (note that this is not the same as the state field)
- Last Activity time = channelInfo => lastActivityTime
currentState and lastActivityTime have a special method of being addressed in the query
Update the channelInfo fields in the query.channelInfo{ currentState lastActivityTime }
Use the Time Widget in the Graph QL Workbench to update the Arguments section of the query selecting From: 1 day ago and To: Now. Then copy the values into the query.
Press the green Send Request button
Scroll through the results in the middle pane of AltairNote that there is a lot of extra information you will not need to satisfy the requirements.
Add filters to the Arguments section of the query to only return agent data for agent which are currently active (logged in) and only return the status information for the telephony channel Inside the curly braces after filter add this compound filter
and:[ {isActive:{equals:true}} {channelInfo:{channelType:{equals:"telephony"}}} ]
Press the green Send Request button
Note there are still fields you will not need to satisfy your requirements
Comment out any fields you will not need by using ctrl + / in front of the field name (you can comment more than one line at a time by selecting multiple lines)
Press the green Send Request button to see the results.Do not close this tab as you will be using it in an upcoming step
Use API to log out agents
Navigate to the Agent Logout API documentation
Log into the developer portal using your admin credentials
Login:
admin login
Password:password In the logoutReason field enter
Admin Logout In the search API results in the the GraphQL Workbench, find your agent's information.
Do you have all of the data we need to to make the logout API call?
You will also need the agentID of the agent you want to log out.
Uncomment out agentId in the fields of the GQL query if they were previously commented out and rerun the request
Enter your agent's agentId in the agentId field
Click on the option to see the Request Body (JSON)
Click on the RUN buttonYou should receive a 202 response
Do not close this tab as you will be using it in an upcoming step
Creating the Web Component
Now that you have the basic data and API elements understood and tested, it is time to put them together in code to visualize the data and orchestrate our actions all while creating a good experience for the end user.
Create a new Web Component
Create a new file in the src directory named
admin-actions.ts In the new file type (not paste) littemplate
Select the Create LitElement Component With lit-html
Add a string Property to hold your Access Token
@property() token?: string
Add an array State to hold the data you will return in the query
@state() agentList = []
Create an Async method to call the search API
async getAgents(){}
Refactor and Export your Graph QL Query
In your GraphQL Workbench browser tab, click on the "suitcase" icon on the left menu bar then click Refactor Query
Change the name of the query from refactored### to:
activeAgents
Compress the query (suitcase menu > Compress)
Copy as cURL (suitcase menu > Copy as cURL)
Import cURL into Postman
Open Postman Click Import
Update Headers in Postman
Uncheck all headers except: Content-Type and Accept
Add an Authorization Header:Key:
Authorization
Value:Bearer placeHolder
Format the request Body
Click on Body
Click the Text dropdown and select JSON
Click Beautify
Turn the request into code
Click the Code button
Select JavaScript - Fetch for the language
Click the settings cog and ensure the Use async/await is toggled on
Copy the code using the copy button.
Add the code to the getAgents method and make these changes
Between the curly braces of the getAgents method, press enter then paste the copied code from postman
In the headers section of the method, find the Authorization header and change "Bearer placeHolder" to`Bearer ${this.token}`
In the raw section which holds the stringified JSON:Change the from variable value to represent a time 24 hours (86400000 ms) before the current time (Date.now()) using a string literal expression:
`${Date.now() - 86400000}`
Change the to variable value to represent the time now:
`${Date.now()}`
Set the type of requestOptions to be an object by adding this notation, after its name and before the equals sign:
: object
In the try section of the method:
Change result to equal:
response.json() instead of response.text()
Return to the GraphQL Workbench to understand how to use the returned data
In the center pane of Altair, copy the data returned from the query
Open JSON Path Finder
Paste the copied data into the left pane
In the right pane, navigate until you find the array of agentSessionsWhat is the JSON path which will return the array of agentSessions?
x.data.agentSession.agentSessions
Back in JSON Path Finder, make the data mimic what you set agentList to in the previous step and only keeping the agentSessions array
On line 4 double click next to the opening bracket of agentSessions to select the entire array.
Copy the array (ctrl + c)
Delete all the data (ctrl + a and delete)
Paste the previously copied array (ctrl + v)
Update the html template in the render method
In the render() method add a few lines between the back ticks in of the
return html``
line
Add a title header for the web component:<h1 class="title">Admin Actions</h1>
Add a button to call the getAgents method:<div><button @click=${this.getAgents}>Refresh Agent List</button></div>
Create a table with headers for Agent Name, Team, Login Time, and StatusAdd a
<table>
tag
Inside the table tags add a table head tag<thead>
Inside the table head tag add table headers for the table as listed above. Example:<th>Agent Name</th>
Create the table body using the data from the agentList array
In this section you are going to use the map method which will preform an action for each object in an array. Each iteration of the map method will produce an object from that index of the array which you will refer to as t and will represent the root of the JSON path. You will be using JSON Path Finder to understand the JSON path addresses as you progress through the fields. You will also be doing some math and other formatting to the returned values to produce a more user friendly table.
Use the map method to render the html of the table body below the </thead>
closing tag
${this.agentList?.map((t: any) => html` <tbody> </tbody> `)}
Inside the tbody tags create the table data cells
Using JSON Path Finder, Create the field for agentName
<td>${t.agentName}</td>
Create the field for Team
<td>${t.teamName}</td>
Create the value of startTime in a human readable datetime stamp
<td>${new Date(t.startTime).toLocaleString()}</td>
Create the value of currentState
<td>${t.channelInfo[0].currentState}</td>
Create the duration of time in the current state
<td>${new Date(Date.now() - t.channelInfo[0].lastActivityTime).toISOString().slice(11, -5)}</td>
Save the TS file
Add to index.html passing the Bearer token as a property
Add the script tag in the header of index.html
<script type="module" src="/src/admin-actions.ts"></script>
Add the custom element tag to the html body of index.html
<admin-actions token="Replace with the token value from the GraphQL Workbench"></admin-actions>
Start the Development server
In the terminal of VS Code run:
yarn dev
Launch the development server index pageYou should see your web component in the browser
Log into the Agent Desktop (so that you will have data to populate the web component)
Launch the Agent Desktop
Login:
admin login
Password:password
Team:team Click the Refresh Agent List button
You should see the list of logged in agents populate
Keep this tab open for additional testing.
Make the output look a better by adding this CSS to the static styles:
:host{ display: flex; flex-direction: column; border: solid 3px var(--md-primary-text-color); padding: 2em; color:var(--md-primary-text-color) } .title{ text-align: center } table{ display:table; border-collapse:collapse; border-spacing: 0; margin-top: 15px; } tr, th, td{ border: solid 1px; text-align: center; } .hidden{ display:none; }
Save the file
Click the Refresh Agent List button
Observe the changes to your web component
Convert the Logout Agents API call into code
In the browser tab with the developer portal open to the agent logout API:
Click on Code Snippets
Select Curl
Click the Copy button in the upper right corner of the code
Open Postman and import the cURL
Click the code button in PostmanMake sure that you have selected Javascript - Fetch with async/await
Copy the code
Create a new async method to log out agents
async logOutAgent(e: any){}
Between the curly braces, press enter then paste the copied code from postman.
Make the following edits to the method code:Change the Authorization header to use the token property
Set the type of requestOptions to be an object by adding this notation, after its name and before the equals sign:: object
Change the agentId in the raw variable to usee.target.value
Add a button to log out the agent in every row of the table
Add a header to the bottom of the
<thead>
section:<th>Action</th>
Add another column to the table data map at the bottom of the<tbody>
section:<td><button value=${t.agentId} @click="${this.logOutAgent}">Log Out</button></td>
Save the file
Click the Refresh Agent List button
Observe the changes to your web component
Adding the Web Component to the agent desktop
Download the Desktop Layout
In Control Hub navigate to Contact Center => Desktop Layouts
Locate the Desktop Layout namedplaceholder for layout name
Click the name of the Desktop Layout to access the detailsNote
- Desktop Layouts are assigned to agents based on the team they have logged in with.
- You can download the existing Desktop Layout JSON if you would like to see the configuration or make edits.
- You can download the default Desktop Layout if you want to start fresh or see what new options may be available on the agent desktop.
- If you do not upload a custom desktop layout, new features will automatically appear on the agent desktop for the assigned teams as they become available.
Click the button to download the default desktop layout.
Open the JSON file in Visual Studio CodeIn the Explorer pane (left pane) of VS Code, click on the Outline to expand it.
You may choose to click on HELLO-WORLD at the top of the Explored pane to collapse the file list of your web components while you explore the Desktop Layout outline.
Hover over OUTLINE to expose the Collapse Allbutton and click on it.
Note
- The JSON layout contains sections for agent, supervisor, and supervisorAgent
- The layout options will be based on the role the user is logging in with in addition to the team they are logging into.
Expand agent > area > advancedHeader and click on advancedHeader to jump to that section
Look at the first entry in the advancedHeader array
- Based on what you have been adding to the index.html file, what similarities do you notice?
- comp - component and is the same as the html tag value you would put in the html body
- script - same as the script src value in the html header
- attributes - similar to properties, but limited to only containing strings, are the same (well similar) as passing a property in the opening component tag in the html
- Notice that the attributes (similar to properties) are being set with a variable
- There is a data provider named STORE which will pass realtime updated to your web component so that it can update.
- STORE attribute documentation for reference
Code
{
"comp": "digital-outbound",
"script": "https://wc.imiengage.io/AIC/engage_aic.js",
"attributes": {
"darkmode": "$STORE.app.darkMode",
"accessToken": "$STORE.auth.accessToken",
"orgId": "$STORE.agent.orgId",
"dataCenter": "$STORE.app.datacenter",
"emailCount": "$STORE.agent.channels.emailCount",
"socialCount": "$STORE.agent.channels.socialCount"
}
}
Adding your widget to a Navigation Page
In the Desktop Layout Outline, Expand agent > area > navigation and click on navigation to jump to that section of the JSON.
After the closing curly brace of the last item in the navigation array and before the closing square bracket of the navigation array:
Add a comma and press enter
Paste this JSON into the layout{ "nav": { "label": "Admin Actions", "icon": "admin-regular", "iconType": "momentumDesign", "navigateTo": "aActions", "align": "top" }, "page": { "id": "aActions", "widgets": { "comp1": { "comp": "admin-actions", "script":"http://localhost:4173/index.js", "attributes": { "token": "$STORE.auth.accessToken" } } }, "layout": { "areas": [["comp1"]], "size": { "cols": [1], "rows": [1] } } } }
Save and update the new layout JSON
Save the file as
yourTeamName .json
Locate your assigned Desktop LayoutUpload the new JSON by dragging the file into the import box or clicking Replace file and navigating to the file using the file explorer Click Save in the lower right corner
Testing
Build and server your widget code
In the terminal at the bottom of VS Code:
Use press ctrl + c to shut down the development server
Build the externally usable code by entering the command:yarn build
Start the preview server with the build command in watch mode by entering the command:yarn game
Log into the agent desktop
Log into the Agent Desktop if you are not already logged in.
Login:
admin login
Password:password
Team:team
Testing steps
The Navigation pane should include the icon you defined in the agent desktop
Click on the icon to show the Admin Actions screenDoes the tool render?
Click the Refresh Agent List button
Do you see a list of logged in users with all of the fields populated?
Click on the avatar (your initials) in the upper right corner of the screen to open the desktop settings
Toggle to dark mode
Does the tool render correctly in both light and dark mode?
Find a buddy next to you in the room and one of you log the other person out using the logout button.
Did the agent get logged out?
Refresh the list using the button to make sure.
Stop the testing server
In the terminal of VS Code press ctrl + c