|
|
|
@ -4,15 +4,33 @@ import * as _ from 'lodash';
|
|
|
|
|
// The following line is important to keep in that format so it can be rendered into the page
|
|
|
|
|
export const config: IDashboardConfig = /*return*/ {
|
|
|
|
|
id: 'cosmosdb_handoff',
|
|
|
|
|
name: 'Cosmos DB conversations',
|
|
|
|
|
name: 'Hand-off to human',
|
|
|
|
|
icon: 'question_answer',
|
|
|
|
|
url: 'cosmosdb_handoff',
|
|
|
|
|
description: 'Conversations with option to hand-off to human',
|
|
|
|
|
description: 'Monitor bot and hand-off to human conversations',
|
|
|
|
|
preview: '/images/bot-framework-preview.png',
|
|
|
|
|
html: `<div>
|
|
|
|
|
<h1>Cosmos DB conversations</h1>
|
|
|
|
|
<p>Displays list of active conversations with a bot using Cosmos DB as the data source.</p>
|
|
|
|
|
<p>If there is a conversation of interest this can be selected to show the option to hand-off to human agent.</p>
|
|
|
|
|
<h1>Hand-off to human</h1>
|
|
|
|
|
<h2>Features</h2>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>
|
|
|
|
|
<p>Displays total users including how many are talking with bot, human agent or waiting for an agent.</p>
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
<p>Displays average time waiting (in secs) for human agent to connect and respond to user,
|
|
|
|
|
including the shortest and longest times.</p>
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
<p>Displays total number of transcripts with bot or with human agent.</p>
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
<p>Displays timeline of conversations with bot and human.</p>
|
|
|
|
|
</li>
|
|
|
|
|
<li>
|
|
|
|
|
<p>Displays list of active conversations with sentiment score.</p>
|
|
|
|
|
<p>If there is a conversation of interest this can be selected to show the option to hand-off to human.</p>
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<p>
|
|
|
|
|
<span>Refer to the </span>
|
|
|
|
|
<span>
|
|
|
|
@ -27,10 +45,6 @@ export const config: IDashboardConfig = /*return*/ {
|
|
|
|
|
'directLine': '',
|
|
|
|
|
'conversationsEndpoint': '',
|
|
|
|
|
'webchatEndpoint': ''
|
|
|
|
|
},
|
|
|
|
|
'cosmos-db': {
|
|
|
|
|
'host': '',
|
|
|
|
|
'key': ''
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
layout: {
|
|
|
|
@ -70,54 +84,267 @@ export const config: IDashboardConfig = /*return*/ {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'botConversations',
|
|
|
|
|
type: 'CosmosDB/Query',
|
|
|
|
|
dependencies: { timespan: 'timespan', queryTimespan: 'timespan:queryTimespan' },
|
|
|
|
|
params: {
|
|
|
|
|
databaseId: 'admin',
|
|
|
|
|
collectionId: 'conversations',
|
|
|
|
|
query: () => `SELECT * FROM conversations c WHERE (c.state = 0 OR c.state = 1) ORDER BY c.state`,
|
|
|
|
|
parameters: []
|
|
|
|
|
id: 'ai',
|
|
|
|
|
type: 'ApplicationInsights/Query',
|
|
|
|
|
dependencies: {
|
|
|
|
|
timespan: 'timespan',
|
|
|
|
|
queryTimespan: 'timespan:queryTimespan',
|
|
|
|
|
granularity: 'timespan:granularity'
|
|
|
|
|
},
|
|
|
|
|
calculated: (result) => {
|
|
|
|
|
if (!Array.isArray(result.Documents)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
const values = result.Documents.reduce((destArray, currentValue) => {
|
|
|
|
|
if (!currentValue.customer || !currentValue.customer.id || !currentValue.customer.conversation ||
|
|
|
|
|
!currentValue.customer.conversation.id || !currentValue.customer.user || !currentValue.transcript) {
|
|
|
|
|
console.warn('Unexpected Document data, missing key properties.', currentValue);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const lastMessage = currentValue.transcript.reverse().find(x => x.from !== 'Bot');
|
|
|
|
|
const value = {
|
|
|
|
|
userId: currentValue.customer.id,
|
|
|
|
|
conversationId: currentValue.customer.conversation.id,
|
|
|
|
|
username: currentValue.customer.user.name || 'Unknown',
|
|
|
|
|
timestamp: lastMessage.timestamp,
|
|
|
|
|
lastMessage: lastMessage.text || '',
|
|
|
|
|
icon: currentValue.state === 1 ? 'perm_identity' : 'memory',
|
|
|
|
|
};
|
|
|
|
|
destArray.push(value);
|
|
|
|
|
return destArray;
|
|
|
|
|
}, []);
|
|
|
|
|
params: {
|
|
|
|
|
table: 'customEvents',
|
|
|
|
|
queries: {
|
|
|
|
|
transcripts: {
|
|
|
|
|
query: () => `where name == 'Transcript'
|
|
|
|
|
| extend conversationId=tostring(customDimensions.userConversationId),
|
|
|
|
|
customerName=tostring(customDimensions.customerName),
|
|
|
|
|
userTime=tostring(customDimensions.timestamp)
|
|
|
|
|
| project conversationId, customerName, timestamp, userTime, customDimensions
|
|
|
|
|
| order by timestamp desc
|
|
|
|
|
| summarize transcripts_count=count(userTime),
|
|
|
|
|
transcripts=makelist(customDimensions) by customerName, conversationId
|
|
|
|
|
| project conversationId, customerName, transcripts_count, transcripts`,
|
|
|
|
|
calculated: (transcripts) => {
|
|
|
|
|
const listTranscripts = transcripts.reduce((destArray, currentValue) => {
|
|
|
|
|
const transcriptsArray = JSON.parse(currentValue.transcripts);
|
|
|
|
|
const lastMessage = transcriptsArray.find(x => x.from !== 'Bot');
|
|
|
|
|
if (!lastMessage) {
|
|
|
|
|
return destArray;
|
|
|
|
|
}
|
|
|
|
|
const lastSentimentScore = lastMessage.sentimentScore || 0.5;
|
|
|
|
|
const value = {
|
|
|
|
|
userId: lastMessage.customerId,
|
|
|
|
|
conversationId: lastMessage.customerConversationId,
|
|
|
|
|
username: lastMessage.customerName || 'Anon',
|
|
|
|
|
timestamp: new Date(lastMessage.timestamp).toUTCString(),
|
|
|
|
|
lastMessage: lastMessage.text || '',
|
|
|
|
|
lastSentimentScore: lastSentimentScore,
|
|
|
|
|
lastSentiment: lastSentimentScore < 0 ? 'error_outline' :
|
|
|
|
|
lastSentimentScore < 0.2 ? 'sentiment_very_dissatisfied' :
|
|
|
|
|
lastSentimentScore < 0.4 ? 'sentiment_dissatisfied' :
|
|
|
|
|
lastSentimentScore < 0.6 ? 'sentiment_neutral' :
|
|
|
|
|
lastSentimentScore < 0.8 ? 'sentiment_satisfied' : 'sentiment_very_satisfied',
|
|
|
|
|
icon: lastMessage.state === 1 ? 'perm_identity' : 'memory',
|
|
|
|
|
};
|
|
|
|
|
destArray.push(value);
|
|
|
|
|
return destArray;
|
|
|
|
|
}, []);
|
|
|
|
|
return {
|
|
|
|
|
'transcripts-values': listTranscripts
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
return { result, values };
|
|
|
|
|
transcriptsTimeWaiting: {
|
|
|
|
|
query: () => `where name == 'Transcript'
|
|
|
|
|
| extend conversationId=tostring(customDimensions.userConversationId),
|
|
|
|
|
customerId=tostring(customDimensions.customerId),
|
|
|
|
|
state=toint(customDimensions.state)
|
|
|
|
|
| where state==1 or state==2
|
|
|
|
|
| order by timestamp asc
|
|
|
|
|
| summarize total=count(), times=makelist(timestamp) by conversationId, customerId, bin(state, 1)
|
|
|
|
|
| project conversationId, customerId, state, startTime=times[0]
|
|
|
|
|
| summarize result=count(state), startEndTimes=makelist(startTime) by conversationId, customerId
|
|
|
|
|
| where result == 2
|
|
|
|
|
| project conversationId, customerId, timeTaken=todatetime(startEndTimes[1])-todatetime(startEndTimes[0])`,
|
|
|
|
|
calculated: (results) => {
|
|
|
|
|
const times = results.reduce((acc, cur) => {
|
|
|
|
|
// converts time hh:mm:ss format to value in seconds
|
|
|
|
|
acc.push(cur.timeTaken.split(':').reverse().reduce((a, c, i) => a + c * Math.pow(60, i), 0));
|
|
|
|
|
return acc;
|
|
|
|
|
}, []);
|
|
|
|
|
const avgTimeWaiting = times.reduce((a, c) => a + c, 0) / times.length;
|
|
|
|
|
const maxTimeWaiting = Math.max(...times);
|
|
|
|
|
const minTimeWaiting = Math.min(...times);
|
|
|
|
|
return {
|
|
|
|
|
'transcriptsAverageTimeWaiting-value': avgTimeWaiting.toFixed(1),
|
|
|
|
|
'transcriptsLongestTimeWaiting-value': maxTimeWaiting.toFixed(1),
|
|
|
|
|
'transcriptsShortestTimeWaiting-value': minTimeWaiting.toFixed(1),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
transcriptsTimeline: {
|
|
|
|
|
query: (dependencies) => {
|
|
|
|
|
var { granularity } = dependencies;
|
|
|
|
|
return `where name == 'Transcript'
|
|
|
|
|
| extend customerName=tostring(customDimensions.customerName),
|
|
|
|
|
text=tostring(customDimensions.text),
|
|
|
|
|
state=toint(customDimensions.state),
|
|
|
|
|
agentName=tostring(customDimensions.agentName),
|
|
|
|
|
from=tostring(customDimensions.from)
|
|
|
|
|
| extend timestamp=todatetime(customDimensions.timestamp)
|
|
|
|
|
| extend states=pack_array('bot','waiting','agent','watching')
|
|
|
|
|
| extend stateLabel=tostring(states[state])
|
|
|
|
|
| where state == 0 or state == 2
|
|
|
|
|
| project timestamp, from, text, customerName, agentName, state, stateLabel
|
|
|
|
|
| summarize transcripts_count=count()
|
|
|
|
|
by bin(timestamp, ${granularity}), state, stateLabel
|
|
|
|
|
| order by timestamp asc`;
|
|
|
|
|
},
|
|
|
|
|
calculated: (results, dependencies) => {
|
|
|
|
|
const totalBot = results.reduce((a, c) => c.state === 0 ? a + c.transcripts_count : a, 0);
|
|
|
|
|
const totalAgent = results.reduce((a, c) => c.state === 2 ? a + c.transcripts_count : a, 0);
|
|
|
|
|
const totalMessages = totalBot + totalAgent;
|
|
|
|
|
// Timeline
|
|
|
|
|
const { timespan } = dependencies;
|
|
|
|
|
const keys = [...new Set(results.reduce((a, c) => { a.push(c.stateLabel); return a; }, []))];
|
|
|
|
|
const timestampKey = 'time'; // NB: required key name for timeline component
|
|
|
|
|
// group by timestamp
|
|
|
|
|
const graphData = results.reduce((a, c) => {
|
|
|
|
|
if (!c.timestamp) {
|
|
|
|
|
console.warn('Invalid date format:', c);
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
const item = a.find(collection => collection[timestampKey] === c.timestamp);
|
|
|
|
|
if (!item) {
|
|
|
|
|
// new time collection
|
|
|
|
|
let collection = {
|
|
|
|
|
count: 0
|
|
|
|
|
};
|
|
|
|
|
collection[timestampKey] = c.timestamp;
|
|
|
|
|
keys.forEach(key => {
|
|
|
|
|
collection[key] = (key !== c.stateLabel) ? 0 : c.transcripts_count;
|
|
|
|
|
});
|
|
|
|
|
a.push(collection);
|
|
|
|
|
} else {
|
|
|
|
|
// merge into time collection
|
|
|
|
|
item.count += c.transcripts_count;
|
|
|
|
|
item[c.stateLabel] += c.transcripts_count;
|
|
|
|
|
}
|
|
|
|
|
return a;
|
|
|
|
|
}, []);
|
|
|
|
|
return {
|
|
|
|
|
'timeline-graphData': graphData,
|
|
|
|
|
'timeline-recipients': keys,
|
|
|
|
|
'timeline-timeFormat': (timespan === '24 hours' ? 'hour' : 'date'),
|
|
|
|
|
'transcriptsBot-value': totalBot,
|
|
|
|
|
'transcriptsAgent-value': totalAgent,
|
|
|
|
|
'transcriptsTotal-value': totalMessages,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
customerTranscripts: {
|
|
|
|
|
query: () => `where name == 'Transcript'
|
|
|
|
|
| summarize
|
|
|
|
|
maxState=max(toint(customDimensions.state))
|
|
|
|
|
by customerConversationId=tostring(customDimensions.userConversationId),
|
|
|
|
|
customerName=tostring(customDimensions.customerName)`,
|
|
|
|
|
calculated: (customerTranscripts) => {
|
|
|
|
|
const bot = customerTranscripts.filter((e) => e.maxState === 0);
|
|
|
|
|
const waiting = customerTranscripts.filter((e) => e.maxState === 1);
|
|
|
|
|
const agent = customerTranscripts.filter((e) => e.maxState === 2);
|
|
|
|
|
return {
|
|
|
|
|
'customerTotal-value': customerTranscripts.length,
|
|
|
|
|
'customerBot-value': bot.length,
|
|
|
|
|
'customerWaiting-value': waiting.length,
|
|
|
|
|
'customerAgent-value': agent.length,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
elements: [
|
|
|
|
|
{
|
|
|
|
|
id: 'customerTotal',
|
|
|
|
|
type: 'Scorecard',
|
|
|
|
|
title: 'Users',
|
|
|
|
|
size: { w: 6, h: 3 },
|
|
|
|
|
dependencies: {
|
|
|
|
|
card_total_heading: '::Total Users',
|
|
|
|
|
card_total_value: 'ai:customerTotal-value',
|
|
|
|
|
card_total_color: '::#666666',
|
|
|
|
|
card_total_icon: '::account_circle',
|
|
|
|
|
card_bot_heading: '::Bot',
|
|
|
|
|
card_bot_value: 'ai:customerBot-value',
|
|
|
|
|
card_bot_color: '::#00FF00',
|
|
|
|
|
card_bot_icon: '::memory',
|
|
|
|
|
card_agent_heading: '::Agent',
|
|
|
|
|
card_agent_value: 'ai:customerAgent-value',
|
|
|
|
|
card_agent_color: '::#0066FF',
|
|
|
|
|
card_agent_icon: '::perm_identity',
|
|
|
|
|
card_waiting_heading: '::Waiting',
|
|
|
|
|
card_waiting_value: 'ai:customerWaiting-value',
|
|
|
|
|
card_waiting_color: '::#FF6600',
|
|
|
|
|
card_waiting_icon: '::more_horiz',
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
id: 'customerWaiting',
|
|
|
|
|
type: 'Scorecard',
|
|
|
|
|
title: 'Waiting Times',
|
|
|
|
|
size: { w: 6, h: 3 },
|
|
|
|
|
dependencies: {
|
|
|
|
|
card_average_heading: '::Average',
|
|
|
|
|
card_average_value: 'ai:transcriptsAverageTimeWaiting-value',
|
|
|
|
|
card_average_color: '::#333333',
|
|
|
|
|
card_average_icon: '::av_timer',
|
|
|
|
|
card_max_heading: '::Longest',
|
|
|
|
|
card_max_value: 'ai:transcriptsLongestTimeWaiting-value',
|
|
|
|
|
card_max_color: '::#ff0000',
|
|
|
|
|
card_max_icon: '::timer',
|
|
|
|
|
card_min_heading: '::Shortest',
|
|
|
|
|
card_min_value: 'ai:transcriptsShortestTimeWaiting-value',
|
|
|
|
|
card_min_color: '::#0066ff',
|
|
|
|
|
card_min_icon: '::timer',
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
id: 'transcriptsTotal',
|
|
|
|
|
type: 'Scorecard',
|
|
|
|
|
title: 'Transcripts',
|
|
|
|
|
size: { w: 2, h: 8 },
|
|
|
|
|
dependencies: {
|
|
|
|
|
card_total_heading: '::Total Msgs',
|
|
|
|
|
card_total_value: 'ai:transcriptsTotal-value',
|
|
|
|
|
card_total_color: '::#666666',
|
|
|
|
|
card_total_icon: '::question_answer',
|
|
|
|
|
card_bot_heading: '::Bot',
|
|
|
|
|
card_bot_value: 'ai:transcriptsBot-value',
|
|
|
|
|
card_bot_color: '::#00FF00',
|
|
|
|
|
card_bot_icon: '::memory',
|
|
|
|
|
card_agent_heading: '::Agent',
|
|
|
|
|
card_agent_value: 'ai:transcriptsAgent-value',
|
|
|
|
|
card_agent_color: '::#0066FF',
|
|
|
|
|
card_agent_icon: '::perm_identity'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
id: 'timelineHandoffConversations',
|
|
|
|
|
type: 'Area',
|
|
|
|
|
title: 'Conversations with bot / human',
|
|
|
|
|
subtitle: 'How many conversations required hand-off to human',
|
|
|
|
|
size: { w: 10, h: 8 },
|
|
|
|
|
dependencies: {
|
|
|
|
|
values: 'ai:timeline-graphData',
|
|
|
|
|
lines: 'ai:timeline-recipients',
|
|
|
|
|
timeFormat: 'ai:timeline-timeFormat'
|
|
|
|
|
},
|
|
|
|
|
props: {
|
|
|
|
|
isStacked: false,
|
|
|
|
|
showLegend: true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
id: 'conversations',
|
|
|
|
|
type: 'Table',
|
|
|
|
|
title: 'Recent Conversations',
|
|
|
|
|
subtitle: 'Monitor bot communications',
|
|
|
|
|
size: { w: 12, h: 12 },
|
|
|
|
|
dependencies: { values: 'botConversations:values' },
|
|
|
|
|
size: { w: 12, h: 19 },
|
|
|
|
|
dependencies: { values: 'ai:transcripts-values' },
|
|
|
|
|
props: {
|
|
|
|
|
cols: [
|
|
|
|
|
{ header: 'Timestamp', field: 'timestamp', type: 'time', format: 'MMM-DD HH:mm:ss' },
|
|
|
|
|
{ header: 'Timestamp', field: 'timestamp', type: 'time', format: 'MMM-DD HH:mm:ss', width: '100px' },
|
|
|
|
|
{ header: 'Last Message', field: 'lastMessage' },
|
|
|
|
|
{ header: 'Last Sentiment', field: 'lastSentiment', type: 'icon', tooltip: 'lastSentimentScore', tooltipPosition: 'right' },
|
|
|
|
|
{ header: 'Username', field: 'username' },
|
|
|
|
|
{ header: 'Status', field: 'icon', type: 'icon' },
|
|
|
|
|
{ type: 'button', value: 'chat', click: 'openTranscriptsDialog' }
|
|
|
|
@ -125,55 +352,75 @@ export const config: IDashboardConfig = /*return*/ {
|
|
|
|
|
},
|
|
|
|
|
actions: {
|
|
|
|
|
openTranscriptsDialog: {
|
|
|
|
|
action: 'dialog:transcripts',
|
|
|
|
|
action: 'dialog:transcriptsDialog',
|
|
|
|
|
params: { title: 'args:username', conversationId: 'args:conversationId', queryspan: 'timespan:queryTimespan' }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
],
|
|
|
|
|
dialogs: [
|
|
|
|
|
{
|
|
|
|
|
id: 'transcripts',
|
|
|
|
|
id: 'transcriptsDialog',
|
|
|
|
|
width: '60%',
|
|
|
|
|
params: ['title', 'conversationId', 'queryspan'],
|
|
|
|
|
dataSources: [
|
|
|
|
|
{
|
|
|
|
|
id: 'transcripts-data',
|
|
|
|
|
type: 'CosmosDB/Query',
|
|
|
|
|
type: 'ApplicationInsights/Query',
|
|
|
|
|
dependencies: {
|
|
|
|
|
conversationId: 'dialog_transcripts:conversationId',
|
|
|
|
|
queryTimespan: 'dialog_transcripts:queryspan',
|
|
|
|
|
username: 'dialog_transcriptsDialog:title',
|
|
|
|
|
conversationId: 'dialog_transcriptsDialog:conversationId',
|
|
|
|
|
queryTimespan: 'dialog_transcriptsDialog:queryspan',
|
|
|
|
|
secret: 'connection:bot-framework.directLine'
|
|
|
|
|
},
|
|
|
|
|
params: {
|
|
|
|
|
databaseId: 'admin',
|
|
|
|
|
collectionId: 'conversations',
|
|
|
|
|
query: ({ conversationId }) => `
|
|
|
|
|
SELECT * FROM conversations c
|
|
|
|
|
WHERE (c.customer.conversation['$id'] = '${conversationId}')`,
|
|
|
|
|
parameters: []
|
|
|
|
|
},
|
|
|
|
|
calculated: (result, dependencies) => {
|
|
|
|
|
if (!result.Documents) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
table: 'customEvents',
|
|
|
|
|
queries: {
|
|
|
|
|
'userConversationTranscripts':
|
|
|
|
|
{
|
|
|
|
|
query: ({ conversationId }) => {
|
|
|
|
|
return `where name == 'Transcript'
|
|
|
|
|
| where customDimensions.customerConversationId == '${conversationId}'
|
|
|
|
|
| extend timestamp=tostring(customDimensions.timestamp)
|
|
|
|
|
| project timestamp,
|
|
|
|
|
text=tostring(customDimensions.text),
|
|
|
|
|
sentimentScore=todouble(customDimensions.sentimentScore),
|
|
|
|
|
from=tostring(customDimensions.from),
|
|
|
|
|
state=toint(customDimensions.state)
|
|
|
|
|
| order by timestamp asc`; },
|
|
|
|
|
calculated: (transcripts, dependencies) => {
|
|
|
|
|
if (!transcripts || transcripts.length < 1) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let values = [];
|
|
|
|
|
let customer = null;
|
|
|
|
|
let body = {};
|
|
|
|
|
let headers = {};
|
|
|
|
|
let disabled = false;
|
|
|
|
|
const { secret } = dependencies;
|
|
|
|
|
const { secret } = dependencies;
|
|
|
|
|
const { conversationId } = dependencies;
|
|
|
|
|
|
|
|
|
|
if (result.Documents.length === 1) {
|
|
|
|
|
let document = result.Documents[0];
|
|
|
|
|
values = document.transcript || [];
|
|
|
|
|
customer = document.customer;
|
|
|
|
|
disabled = document.state !== 0 ? true : false;
|
|
|
|
|
body = { 'conversationId': customer.conversation.id, };
|
|
|
|
|
headers = { 'Authorization': `Bearer ${secret}` };
|
|
|
|
|
let values = transcripts || [];
|
|
|
|
|
let body, headers = {};
|
|
|
|
|
let disabled = transcripts[transcripts.length - 1].state !== 0 ? true : false;
|
|
|
|
|
|
|
|
|
|
values.map(v => {
|
|
|
|
|
const lastSentimentScore = v.sentimentScore || 0.5;
|
|
|
|
|
v['sentiment'] = lastSentimentScore < 0 ? 'error_outline' :
|
|
|
|
|
lastSentimentScore < 0.2 ? 'sentiment_very_dissatisfied' :
|
|
|
|
|
lastSentimentScore < 0.4 ? 'sentiment_dissatisfied' :
|
|
|
|
|
lastSentimentScore < 0.6 ? 'sentiment_neutral' :
|
|
|
|
|
lastSentimentScore < 0.8 ? 'sentiment_satisfied' : 'sentiment_very_satisfied';
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
body = {
|
|
|
|
|
'conversationId': conversationId,
|
|
|
|
|
};
|
|
|
|
|
headers = {
|
|
|
|
|
'Authorization': `Bearer ${secret}`
|
|
|
|
|
};
|
|
|
|
|
return { 'values': values, 'headers': headers, 'body': body, 'disabled': disabled };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return { values, customer, headers, body, disabled };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
@ -191,7 +438,7 @@ export const config: IDashboardConfig = /*return*/ {
|
|
|
|
|
conversationsEndpoint: 'connection:bot-framework.conversationsEndpoint'
|
|
|
|
|
},
|
|
|
|
|
props: {
|
|
|
|
|
url: ({conversationsEndpoint}) => `${conversationsEndpoint}`,
|
|
|
|
|
url: ({ conversationsEndpoint }) => `${conversationsEndpoint}`,
|
|
|
|
|
method: 'POST',
|
|
|
|
|
disableAfterFirstClick: true,
|
|
|
|
|
icon: 'person',
|
|
|
|
@ -204,8 +451,8 @@ export const config: IDashboardConfig = /*return*/ {
|
|
|
|
|
title: 'Open Webchat',
|
|
|
|
|
size: { w: 2, h: 1 },
|
|
|
|
|
location: { x: 2, y: 0 },
|
|
|
|
|
dependencies: {
|
|
|
|
|
token: 'connection:bot-framework.directLine',
|
|
|
|
|
dependencies: {
|
|
|
|
|
token: 'connection:bot-framework.directLine',
|
|
|
|
|
webchatEndpoint: 'connection:bot-framework.webchatEndpoint',
|
|
|
|
|
dependsOn: 'transcripts-data:disabled'
|
|
|
|
|
},
|
|
|
|
@ -227,11 +474,13 @@ export const config: IDashboardConfig = /*return*/ {
|
|
|
|
|
rowClassNameField: 'from',
|
|
|
|
|
cols: [
|
|
|
|
|
{ header: 'Timestamp', field: 'timestamp', type: 'time', format: 'MMM-DD HH:mm:ss', width: '50px' },
|
|
|
|
|
{ header: 'Sentiment', field: 'sentiment', tooltip: 'sentimentScore', type: 'icon', width: '50px', tooltipPosition: 'right' },
|
|
|
|
|
{ header: 'Text', field: 'text' }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
]
|
|
|
|
|
};
|