text: return {
  	id: "bot_analytics_inst",
  	name: "Bot Analytics Instrumented Dashboard",
  	icon: "dashboard",
  	url: "bot_analytics_inst",
  	description: "Microsoft Bot Framework based analytics",
  	preview: "/images/bot-instrumented.png",
  	category: "Bots",
  	featured: true,
  	html: `<div>
        This dashboard is built to view events sent by Microsoft Bot Framework based bot.
        <br/>
        <br/>
        <h2>Getting the data to show</h2>
        <p>
          To see all the capabilities of this dashboard, it is recommended to integrate you bot with one of the following:
          <br/>
          <a href="https://github.com/Azure/botbuilder-instrumentation" target="_blank">
            Node.js Telemetry Plugin
          </a>
          <br/>
          <a href="https://github.com/Azure/bot-sample-telemetry" target="_blank">C# Telemetry Plugin</a><br/>
          This will enable the bot to send additional telemetry information to Application Insights.
        </p>
        <br/>
        <h2>Additional Learnings</h2>
        <p>
          This dashboard uses 
          <a href="https://docs.microsoft.com/en-us/azure/application-insights/app-insights-analytics" target="_blank">
            Application Insights Analytics
          </a>.
          <br/>
          You can also run queries directly using 
          <a href="https://dev.applicationinsights.io/apiexplorer/query" target="_blank">API Explorer</a>
        </p>
      </div>`,
  	config: {
  		connections: { "application-insights": { appId: "a",apiKey: "a" } },
  		layout: {
  			isDraggable: true,
  			isResizable: true,
  			rowHeight: 30,
  			verticalCompact: false,
  			cols: { lg: 12,md: 10,sm: 6,xs: 4,xxs: 2 },
  			breakpoints: { lg: 1200,md: 996,sm: 768,xs: 480,xxs: 0 }
  		}
  	},
  	dataSources: [
  		{
  			id: "timespan",
  			type: "Constant",
  			params: { selectedValue: "2019-06-12T00:00:00.000Z/2019-06-12T23:59:59.999Z" },
  			calculated: (state, dependencies) => {
          var queryTimespan =  state.selectedValue;
          var dateRangeArray = state.selectedValue.split('/');
          
          var initDate = new Date(dateRangeArray[0]);
          var endDate = new Date(dateRangeArray[1]);
          var diffTime = endDate.getTime() - initDate.getTime();
          var diffDays = diffTime / (3600 * 1000 * 24);
    
          var timeFormat = (diffDays > 3) ? 'date' : 'hour' ;
          
          return {
            queryTimespan: state.selectedValue,
            startTimespan: dateRangeArray[0],
            endTimespan: dateRangeArray[1],
            'timeline-timeFormat': timeFormat,
          };
        }
  		},
  		{
  			id: "granularity",
  			type: "Constant",
  			params: { values: ["1h","2h","6h","12h","1d","7d","30d"],selectedValue: "1h" },
  			calculated: (state, dependencies) => {
          return { granularity: state.selectedValue };
        }
  		},
  		{
  			id: "modes",
  			type: "Constant",
  			params: { values: ["messages","users"],selectedValue: "messages" },
  			format: "flags"
  		},
  		{
  			id: "filters",
  			type: "ApplicationInsights/Query",
  			dependencies: { timespan: "timespan",queryTimespan: "timespan:queryTimespan",granularity: "granularity" },
  			params: {
  				table: "customEvents",
  				queries: {
  					channels: {
  						query: () => `
                where name == 'MBFEvent.UserMessage' 
                | extend value=tostring(customDimensions.channel) 
                | summarize channel_count=count() by value 
                | order by channel_count`,
  						format: "filter"
  					},
  					intents: {
  						query: () => `
                extend value=tostring(customDimensions.intent), cslen=customDimensions.callstack_length 
                | where name=='MBFEvent.Intent' and (cslen == 0 or strlen(cslen) == 0) and strlen(value) > 0 
                | summarize intent_count=count() by value 
                | order by intent_count`,
  						format: "filter"
  					}
  				}
  			}
  		},
  		{
  			id: "ai",
  			type: "ApplicationInsights/Query",
  			dependencies: {
  				timespan: "timespan",
  				queryTimespan: "timespan:queryTimespan",
  				granularity: "granularity",
  				selectedChannels: "filters:channels-values-selected",
  				selectedIntents: "filters:intents-values-selected"
  			},
  			params: {
  				table: "customEvents",
  				queries: {
  					timeline: {
  						query: ({ granularity }) => `
  								where name == 'MBFEvent.UserMessage' 
                  | summarize count=count() by bin(timestamp, ${granularity}), 
                    name, channel=tostring(customDimensions.channel) 
  								| order by timestamp asc `,
  						filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }],
  						format: { type: "timeline",args: { timeField: "timestamp",lineField: "channel",valueField: "count" } }
  					},
  					"users-timeline": {
  						query: ({ granularity }) => `
                  where name == 'MBFEvent.UserMessage' 
                  | summarize count=dcount(tostring(customDimensions.userId)) by bin(timestamp, ${granularity}), 
                    name, channel=tostring(customDimensions.channel) 
                  | order by timestamp asc`,
  						filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }],
  						format: { type: "timeline",args: { timeField: "timestamp",lineField: "channel",valueField: "count" } }
  					},
  					intents: {
  						query: () => `
                extend cslen=customDimensions.callstack_length, value=tostring(customDimensions.intent) 
                | where name=='MBFEvent.Intent' and (cslen == 0 or strlen(cslen) == 0) and strlen(value) > 0 
                | summarize count=count() by value`,
  						filters: [{ dependency: "selectedIntents",queryProperty: "customDimensions.intent" }],
  						format: "bars"
  					},
  					conversions: {
  						query: () => `
                extend successful=(customDimensions.successful == 'true') 
                | summarize count_start=countif(name == 'MBFEvent.StartTransaction'), 
                  count_end=countif(name == 'MBFEvent.EndTransaction' and successful) by name, successful 
                | where name == 'MBFEvent.StartTransaction' or (name == 'MBFEvent.EndTransaction' and successful) 
                | extend count_start=toreal(count_start), count_end=toreal(count_end) 
                | summarize count=sum(count_start) * 100 / sum(count_end)`,
  						filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }],
  						format: {
  							type: "scorecard",
  							args: { postfix: "%",thresholds: [{ value: 0,color: "#2196F3",icon: "input",heading: "Conversions" }] }
  						}
  					},
  					mapActivity: {
  						query: () => `
                where name=='MBFEvent.UserMessage' 
                | extend location=strcat(client_City, ', ', client_CountryOrRegion) 
                | summarize location_count=count() by location 
                | extend popup=strcat('<b>', location, '</b><br />', location_count, ' messages') `,
  						filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }]
  					},
  					sentiments: {
  						query: () => `
                where name startswith 'MBFEvent.Sentiment' 
                | extend score=customDimensions.score
                | summarize count=100 * avg(todouble(score))`,
  						format: {
  							type: "scorecard",
  							args: {
  								postfix: "%",
  								thresholds: [
  									{ value: 0,color: "#D50000",icon: "sentiment_dissatisfied",heading: "Sentiment" },
  									{ value: 40,color: "#FF9810",icon: "sentiment_neutral",heading: "Sentiment" },
  									{ value: 60,color: "#AEEA00",icon: "sentiment_satisfied",heading: "Sentiment" }
  								],
  								subvalueThresholds: [
  									{ value: 0,subheading: "Negative" },
  									{ value: 40,subheading: "Neutral" },
  									{ value: 60,subheading: "Positive" }
  								]
  							}
  						}
  					}
  				}
  			}
  		},
  		{
  			id: "errors",
  			type: "ApplicationInsights/Query",
  			dependencies: { timespan: "timespan",queryTimespan: "timespan:queryTimespan" },
  			params: { query: () => `exceptions | summarize count=count()` },
  			format: {
  				type: "scorecard",
  				args: {
  					thresholds: [
  						{ value: 0,color: "#AEEA00",heading: "Errors",icon: "done",subheading: "Avg" },
  						{ value: 1,color: "#D50000",heading: "Errors",icon: "bug_report",subheading: "Avg" }
  					]
  				}
  			}
  		},
  		{
  			id: "retention",
  			type: "ApplicationInsights/Query",
  			dependencies: { timespan: "timespan",selectedTimespan: "timespan:queryTimespan",queryTimespan: "::P90D" },
  			params: {
  				query: () => `
            customEvents 
            | where name=='MBFEvent.UserMessage' 
            | extend uniqueUser=tostring(customDimensions.userId) 
            | summarize oldestVisit=min(timestamp), lastVisit=max(timestamp) by uniqueUser 
            | summarize
              totalUnique = dcount(uniqueUser),
              totalUniqueUsersIn24hr = countif(lastVisit > ago(24hr)),
              totalUniqueUsersIn7d = countif(lastVisit > ago(7d)),
              totalUniqueUsersIn30d = countif(lastVisit > ago(30d)),
              returning24hr = countif(lastVisit > ago(24hr) and oldestVisit <= ago(24hr)),
              returning7d = countif(lastVisit > ago(7d) and oldestVisit <= ago(7d)),
              returning30d = countif(lastVisit > ago(30d) and oldestVisit <= ago(30d))`
  			},
  			format: "retention"
  		}
  	],
  	filters: [
  		{
  			type: "DatePickerFilter",
  			title: "Timespan",
  			dependencies: { selectedValue: "timespan" },
  			actions: { onChange: "timespan:updateSelectedValue" },
  			first: true
  		},
  		{
  			type: "TextFilter",
  			title: "Granularity",
  			dependencies: { selectedValue: "granularity",values: "granularity:values" },
  			actions: { onChange: "granularity:updateSelectedValue" },
  			first: true
  		},
  		{
  			type: "TextFilter",
  			title: "Mode",
  			source: "modes",
  			actions: { onChange: "modes:updateSelectedValue" },
  			first: true
  		},
  		{
  			type: "MenuFilter",
  			title: "Channels",
  			subtitle: "Select channels",
  			source: "filters:channels",
  			actions: { onChange: "filters:updateSelectedValues:channels-values-selected" },
  			first: true
  		},
  		{
  			type: "MenuFilter",
  			title: "Intents",
  			subtitle: "Select intents",
  			source: "filters:intents",
  			actions: { onChange: "filters:updateSelectedValues:intents-values-selected" },
  			first: true
  		}
  	],
  	elements: [
  		{
  			id: "timeline",
  			type: "Timeline",
  			title: "Message Rate",
  			subtitle: "How many messages were sent per timeframe",
  			size: { w: 5,h: 8 },
  			source: "ai:timeline",
  			dependencies: { visible: "modes:messages" }
  		},
  		{
  			id: "timeline",
  			type: "Timeline",
  			title: "Users Rate",
  			subtitle: "How many users were sent per timeframe",
  			size: { w: 5,h: 8 },
  			source: "ai:users-timeline",
  			dependencies: { visible: "modes:users" }
  		},
  		{
  			id: "channels",
  			type: "PieData",
  			title: "Channel Usage",
  			subtitle: "Total messages sent per channel",
  			size: { w: 3,h: 8 },
  			source: "ai:timeline",
  			dependencies: { visible: "modes:messages" },
  			props: { showLegend: true,compact: true,entityType: "Messages" }
  		},
  		{
  			id: "channels",
  			type: "PieData",
  			title: "Channel Usage (Users)",
  			subtitle: "Total users sent per channel",
  			size: { w: 3,h: 8 },
  			source: "ai:users-timeline",
  			dependencies: { visible: "modes:users" },
  			props: { showLegend: true,compact: true,entityType: "Users" }
  		},
  		{
  			id: "scores",
  			type: "Scorecard",
  			size: { w: 4,h: 3 },
  			source: { errors: "errors",sentiment: "ai:sentiments",conversions: "ai:conversions" },
  			dependencies: {
  				card_errors_tooltip: "::Total errors",
  				card_errors_onClick: "::onErrorsClick",
  				card_users_value: "retention:total",
  				card_users_heading: "::Users",
  				card_users_icon: "::account_circle",
  				card_users_subvalue: "retention:returning",
  				card_users_subheading: "::Returning",
  				card_users_tooltip: "::Total users and retention",
  				card_users_onClick: "::onUsersClick",
  				card_sentiment_tooltip: "::Average sentiment",
  				card_sentiment_onClick: "::onSentimentsClick",
  				card_conversions_tooltip: "::Percentage of user interactions completed with a conversion"
  			},
  			actions: {
  				onErrorsClick: {
  					action: "dialog:errors",
  					params: {
  						title: "args:heading",
  						type: "args:type",
  						innermostMessage: "args:innermostMessage",
  						queryspan: "timespan:queryTimespan"
  					}
  				},
  				onUsersClick: { action: "dialog:userRetention",params: { title: "args:heading",queryspan: "::P90D" } },
  				onSentimentsClick: {
  					action: "dialog:sentimentConversations",
  					params: { title: "args:heading",queryspan: "timespan:queryTimespan" }
  				}
  			}
  		},
  		{
  			id: "intents",
  			type: "BarData",
  			title: "Intents Graph",
  			subtitle: "Intents usage per time",
  			size: { w: 4,h: 8 },
  			source: "ai:intents",
  			actions: {
  				onBarClick: {
  					action: "dialog:intentsDialog",
  					params: { title: "args:value",intent: "args:value",queryspan: "timespan:queryTimespan" }
  				}
  			}
  		},
  		{
  			id: "timeline-area",
  			type: "Area",
  			title: "Message Rate",
  			subtitle: "How many messages were sent per timeframe",
  			size: { w: 4,h: 8 },
  			source: "ai:timeline",
  			props: { isStacked: true,showLegend: false }
  		},
  		{
  			id: "map",
  			type: "MapData",
  			title: "Map Activity",
  			subtitle: "Monitor regional activity",
  			size: { w: 4,h: 13 },
  			location: { x: 9,y: 1 },
  			source: "ai:mapActivity",
  			props: { mapProps: { zoom: 1,maxZoom: 6 },searchLocations: true }
  		}
  	],
  	dialogs: [
  		{
  			id: "intentsDialog",
  			width: "70%",
  			params: ["title","intent","queryspan"],
  			dataSources: [
  				{
  					id: "intents-data",
  					type: "ApplicationInsights/Query",
  					dependencies: {
  						intent: "dialog_intentsDialog:intent",
  						queryTimespan: "dialog_intentsDialog:queryspan",
  						timespan: "timespan",
  						granularity: "granularity"
  					},
  					params: {
  						table: "customEvents",
  						queries: {
  							"intent-usage-timeline": {
  								query: ({ intent, granularity }) => `
                    extend intent=(customDimensions.intent)
                    | where timestamp > ago(30d) and intent =~ '${intent}'
                    | summarize count=count() by bin(timestamp, ${granularity})
                    | order by timestamp`,
  								format: { type: "timeline",args: { timeField: "timestamp",lineField: "intent",valueField: "count" } }
  							},
  							"entities-usage": {
  								query: ({ intent }) => `
                    extend conversation=tostring(customDimensions.conversationId), 
                      entityType=tostring(customDimensions.entityType), 
                      entityValue=tostring(customDimensions.entityValue), 
                      intent=customDimensions.intent 
                    | where name=='MBFEvent.Entity' and intent =~'${intent}' 
                    | project conversation, entityType, entityValue, intent 
                    | summarize count=count() by entityType, entityValue`,
  								format: {
  									type: "bars",
  									args: { barsField: "entityType",seriesField: "entityValue",valueField: "count",threshold: 10 }
  								}
  							},
  							"total-conversations": {
  								query: ({ intent }) => `
                    extend conversation=tostring(customDimensions.conversationId), intent=customDimensions.intent 
                    | where name=='MBFEvent.Intent' and intent =~ '${intent}' 
                    | summarize count_intents=count() by conversation  
                    | count`,
  								format: {
  									type: "scorecard",
  									args: {
  										countField: "Count",
  										thresholds: [{ value: 0,color: "#2196F3",icon: "chat",heading: "Conversations" }]
  									}
  								}
  							},
  							"intent-utterances": {
  								query: ({ intent }) => `
                    extend conversation=tostring(customDimensions.conversationId), 
                      intent=customDimensions.intent,
                      text=substring(customDimensions.text, 0, 50) 
                    | where name=='MBFEvent.Intent' and intent =~ '${intent}' 
                    | summarize count_utterances=count(), maxTimestamp=max(timestamp) by text 
                    | order by count_utterances 
                    | top 5 by count_utterances `
  							}
  						}
  					}
  				},
  				{
  					id: "intentSentiments",
  					type: "ApplicationInsights/Query",
  					dependencies: { intent: "dialog_intentsDialog:intent",queryTimespan: "dialog_intentsDialog:queryspan" },
  					params: {
  						query: ({ intent }) => `
                customEvents 
                | extend intent=customDimensions.intent 
                | where name startswith 'MBFEvent.Intent' and intent =~ '${intent}' 
                | extend timestamp=tostring(customDimensions.timestamp),
                  conversation=tostring(customDimensions.conversationId),
                  userId=tostring(customDimensions.userId) 
                | join kind= leftouter (
                  customEvents 
                  | where name startswith 'MBFEvent.Sentiment' 
                  | extend timestamp=tostring(customDimensions.timestamp), 
                    sentiment=todouble(customDimensions.score),
                    conversation=tostring(customDimensions.conversationId),
                    userId=tostring(customDimensions.userId)
                ) on timestamp, userId, conversation 
                | summarize count=avg(sentiment)
              `
  					},
  					format: {
  						type: "scorecard",
  						args: {
  							thresholds: [
  								{ value: 0,color: "#D50000",icon: "sentiment_dissatisfied",heading: "Sentiment" },
  								{ value: 40,color: "#FF9810",icon: "sentiment_neutral",heading: "Sentiment" },
  								{ value: 60,color: "#AEEA00",icon: "sentiment_satisfied",heading: "Sentiment" }
  							],
  							subvalueThresholds: [
  								{ value: 0,subheading: "Negative" },
  								{ value: 40,subheading: "Neutral" },
  								{ value: 60,subheading: "Positive" }
  							]
  						}
  					}
  				}
  			],
  			elements: [
  				{
  					id: "entity-usage",
  					type: "BarData",
  					title: "Entity count appearances in intent",
  					subtitle: "Entity usage and count for the selected intent",
  					size: { w: 6,h: 8 },
  					source: "intents-data:entities-usage"
  				},
  				{
  					id: "utterances",
  					type: "Table",
  					size: { w: 4,h: 8 },
  					dependencies: { values: "intents-data:intent-utterances" },
  					props: {
  						cols: [
  							{ header: "Top Utterances",width: "200px",field: "text" },
  							{ header: "Count",field: "count_utterances",type: "number" }
  						]
  					}
  				},
  				{
  					id: "intent-timeline",
  					type: "Timeline",
  					title: "Message Rate",
  					subtitle: "How many messages were sent per timeframe",
  					size: { w: 8,h: 8 },
  					source: "intents-data:intent-usage-timeline"
  				},
  				{
  					id: "conversations-count",
  					type: "Scorecard",
  					size: { w: 2,h: 8 },
  					source: { conversations: "intents-data:total-conversations",sentiment: "intentSentiments" },
  					dependencies: { card_conversations_onClick: "::onConversationsClick" },
  					actions: {
  						onConversationsClick: {
  							action: "dialog:intentConversations",
  							params: {
  								title: "dialog_intentsDialog:title",
  								intent: "dialog_intentsDialog:intent",
  								queryspan: "dialog_intentsDialog:queryspan"
  							}
  						}
  					}
  				}
  			]
  		},
  		{
  			id: "intentConversations",
  			width: "60%",
  			params: ["title","intent","queryspan"],
  			dataSources: [
  				{
  					id: "conversations-data",
  					type: "ApplicationInsights/Query",
  					dependencies: {
  						intent: "dialog_intentConversations:intent",
  						queryTimespan: "dialog_intentConversations:queryspan"
  					},
  					params: {
  						query: ({ intent }) => ` 
                customEvents
                | extend conversation=tostring(customDimensions.conversationId), intent=customDimensions.intent
                | where name=='MBFEvent.Intent' and intent =~ '${intent}'
                | summarize count=count(), maxTimestamp=max(timestamp) by conversation
                | order by maxTimestamp`,
  						mappings: { id: (val, row, idx) => `Conversation ${idx}` }
  					}
  				}
  			],
  			elements: [
  				{
  					id: "conversations-list",
  					type: "Table",
  					title: "Conversations",
  					size: { w: 12,h: 16 },
  					dependencies: { values: "conversations-data" },
  					props: {
  						cols: [
  							{ header: "Conversation Id",field: "id" },
  							{ header: "Last Message",field: "maxTimestamp",type: "time",format: "MMM-DD HH:mm:ss" },
  							{ header: "Count",field: "count" },
  							{ type: "button",value: "chat",click: "openMessagesDialog" }
  						]
  					},
  					actions: {
  						openMessagesDialog: {
  							action: "dialog:messages",
  							params: {
  								title: "args:id",
  								conversation: "args:conversation",
  								intent: "dialog_intentConversations:intent",
  								queryspan: "timespan:queryTimespan"
  							}
  						}
  					}
  				}
  			]
  		},
  		{
  			id: "sentimentConversations",
  			width: "60%",
  			params: ["title","queryspan"],
  			dataSources: [
  				{
  					id: "sentiment-conversations-data",
  					type: "ApplicationInsights/Query",
  					dependencies: { queryTimespan: "dialog_sentimentConversations:queryspan" },
  					params: {
  						query: () => ` 
                customEvents
                | extend conversation=tostring(customDimensions.conversationId), 
                  intent=customDimensions.intent,
                  timestamp=tostring(customDimensions.timestamp),
                  userId=tostring(customDimensions.userId)
                | where name=='MBFEvent.Intent'
                | join kind=leftouter (
                  customEvents 
                  | where name startswith 'MBFEvent.Sentiment' 
                  | extend timestamp=tostring(customDimensions.timestamp), 
                    sentiment=todouble(customDimensions.score),
                    conversation=tostring(customDimensions.conversationId),
                    userId=tostring(customDimensions.userId)
                ) on timestamp, userId, conversation
                | summarize count=count(), sentiment=avg(sentiment), maxTimestamp=max(timestamp) by conversation
                | extend color=iff(sentiment > 0.6, 'green', iff(sentiment < 0.4, 'red', 'yellow')), 
                  icon=iff(sentiment > 0.6, 'sentiment_satisfied', 
                  iff(sentiment < 0.4, 'sentiment_dissatisfied', 'sentiment_neutral'))
                | order by sentiment`,
  						mappings: { id: (val, row, idx) => `Conversation ${idx}` }
  					},
  					calculated: ({ values }) => {
              return {
                top5Positive: _.take(values, 5),
                top5Negative: _.takeRight(values, 5)
              };
            }
  				}
  			],
  			elements: [
  				{
  					id: "top5positive",
  					type: "Table",
  					size: { w: 5,h: 8 },
  					dependencies: { values: "sentiment-conversations-data:top5Positive" },
  					props: {
  						compact: true,
  						cols: [
  							{ header: "Top 5 Positive",field: "id" },
  							{ header: null,width: "10px",field: "icon",type: "icon",color: "color" },
  							{ header: "Last Message",field: "maxTimestamp",type: "time",format: "MMM-DD HH:mm:ss" },
  							{ header: "Count",width: "10px",field: "count" },
  							{ type: "button",width: "10px",value: "chat",click: "openMessagesDialog" }
  						]
  					},
  					actions: {
  						openMessagesDialog: {
  							action: "dialog:messages",
  							params: {
  								title: "args:id",
  								conversation: "args:conversation",
  								queryspan: "timespan:queryTimespan",
  								intent: "::"
  							}
  						}
  					}
  				},
  				{
  					id: "top5negative",
  					type: "Table",
  					size: { w: 5,h: 8 },
  					dependencies: { values: "sentiment-conversations-data:top5Negative" },
  					props: {
  						compact: true,
  						cols: [
  							{ header: "Top 5 Negative",field: "id" },
  							{ header: null,width: "10px",field: "icon",type: "icon",color: "color" },
  							{ header: "Last Message",field: "maxTimestamp",type: "time",format: "MMM-DD HH:mm:ss" },
  							{ header: "Count",field: "count" },
  							{ type: "button",value: "chat",click: "openMessagesDialog" }
  						]
  					},
  					actions: {
  						openMessagesDialog: {
  							action: "dialog:messages",
  							params: {
  								title: "args:id",
  								conversation: "args:conversation",
  								queryspan: "timespan:queryTimespan",
  								intent: "::"
  							}
  						}
  					}
  				}
  			]
  		},
  		{
  			id: "messages",
  			width: "50%",
  			params: ["title","conversation","intent","queryspan"],
  			dataSources: [
  				{
  					id: "messages-data",
  					type: "ApplicationInsights/Query",
  					dependencies: {
  						conversation: "dialog_messages:conversation",
  						optional_intent: "dialog_messages:intent",
  						queryTimespan: "dialog_messages:queryspan"
  					},
  					params: {
  						query: ({ conversation, optional_intent }) => ` 
                customEvents
                | extend intent=tostring(customDimensions.intent), 
                  conversation=tostring(customDimensions.conversationId), 
                  eventTimestamp=tostring(customDimensions.timestamp),
                  userId=tostring(customDimensions.userId)
                | where name == 'MBFEvent.UserMessage' and conversation == '${conversation}'
                | join kind= leftouter (
                  customEvents
                  | extend sentiment=tostring(customDimensions.score),
                    eventTimestamp=tostring(customDimensions.timestamp), 
                    conversation=tostring(customDimensions.conversationId),
                    userId=tostring(customDimensions.userId)
                  | where name == 'MBFEvent.Sentiment' and conversation == '${conversation}'
                ) on eventTimestamp, conversation, userId
                ${optional_intent && `
                  | union (
                    customEvents
                    | extend conversation=tostring(customDimensions.conversationId), 
                      intent=tostring(customDimensions.intent),
                      eventTimestamp=tostring(customDimensions.timestamp),
                      userId=tostring(customDimensions.userId)
                    | where name == 'MBFEvent.Intent' 
                      and intent == '${optional_intent}' and conversation == '${conversation}'
                  )
                ` || ''}
                | union (
                  customEvents
                  | extend conversation=tostring(customDimensions.conversationId), 
                    intent=tostring(customDimensions.intent), 
                    eventTimestamp=tostring(customDimensions.timestamp),
                    userId=tostring(customDimensions.userId)
                  | where name == 'MBFEvent.BotMessage' and conversation == '${conversation}'
                )
                | project timestamp, eventName=name, message=customDimensions.text, 
                  customDimensions.userName, userId, intent, sentiment
                | order by timestamp asc
                `
  					},
  					calculated: (state, dependencies) => {
              let { values } = state;
  
              if (!values) { return; }
  
              _.forEach(values, (msg, index) => {
                msg.message = msg.message;
                msg.icon = isNaN(parseInt(msg.sentiment, 10)) ? '' :
                  msg.sentiment > 0.6 ? 'sentiment_satisfied' :
                    msg.sentiment < 0.4 ? 'sentiment_dissatisfied' : '';
                msg.color = isNaN(parseInt(msg.sentiment, 10)) ? '' :
                  msg.sentiment > 0.6 ? '#AEEA00' :
                    msg.sentiment < 0.4 ? '#D50000' : '';
  
                if (msg.eventName === 'MBFEvent.UserMessage') {
                  let i = +index;
                  let j = i + 1;
                  while (j <= i + 5 && j < values.length) {
                    let intent = values[j];
                    if (intent.eventName === 'MBFEvent.Intent' && intent.message === msg.message && intent.intent) {
                      msg.intent = intent.intent;
                      msg.message = '[' + msg.intent + '] ' + msg.message;
                      msg.eventName = msg.eventName + ' intent';
                      break;
                    }
                    j++;
                  }
                }
              });
  
              let chat = values.filter(msg => msg.eventName !== 'MBFEvent.Intent');
  
              return { chat };
            }
  				}
  			],
  			elements: [
  				{
  					id: "messages-list",
  					type: "Table",
  					title: "Messages",
  					size: { w: 12,h: 16 },
  					dependencies: { values: "messages-data:chat" },
  					props: {
  						rowClassNameField: "eventName",
  						cols: [
  							{ header: "Timestamp",width: "50px",field: "timestamp",type: "time",format: "MMM-DD HH:mm:ss" },
  							{ width: "10px",field: "icon",type: "icon",color: "color" },
  							{ header: "Message",width: "500px",field: "message" }
  						]
  					}
  				}
  			]
  		},
  		{
  			id: "errors",
  			width: "90%",
  			params: ["title","queryspan"],
  			dataSources: [
  				{
  					id: "errors-group",
  					type: "ApplicationInsights/Query",
  					dependencies: { queryTimespan: "dialog_errors:queryspan" },
  					params: {
  						query: () => ` 
                exceptions
                | summarize error_count=count() by type, innermostMessage
                | project type, innermostMessage, error_count
                | order by error_count desc `
  					}
  				},
  				{
  					id: "errors-selection",
  					type: "ApplicationInsights/Query",
  					dependencies: {
  						queryTimespan: "dialog_errors:queryspan",
  						type: "args:type",
  						innermostMessage: "args:innermostMessage"
  					},
  					params: {
  						query: ({ type, innermostMessage }) => `
                exceptions
                | where type == "${type}" 
                | where innermostMessage == "${innermostMessage}" 
                | extend conversationId=customDimensions['Conversation ID'] 
                | project type, innermostMessage, handledAt, conversationId, operation_Id `
  					}
  				}
  			],
  			elements: [
  				{
  					id: "errors-list",
  					type: "SplitPanel",
  					title: "Errors",
  					size: { w: 12,h: 16 },
  					dependencies: { groups: "errors-group",values: "errors-selection" },
  					props: {
  						cols: [
  							{ header: "Type",field: "type",secondaryHeader: "Message",secondaryField: "innermostMessage" },
  							{
  								header: "Conversation Id",
  								field: "conversationId",
  								secondaryHeader: "Operation Id",
  								secondaryField: "operation_Id"
  							},
  							{ header: "HandledAt",field: "handledAt" },
  							{ type: "button",value: "more",click: "openErrorDetail" }
  						],
  						group: { field: "type",secondaryField: "innermostMessage",countField: "error_count" }
  					},
  					actions: {
  						select: {
  							action: "errors-selection:updateDependencies",
  							params: {
  								title: "args:type",
  								type: "args:type",
  								innermostMessage: "args:innermostMessage",
  								queryspan: "timespan:queryTimespan"
  							}
  						},
  						openErrorDetail: {
  							action: "dialog:errordetail",
  							params: {
  								title: "args:operation_Id",
  								type: "args:type",
  								innermostMessage: "args:innermostMessage",
  								handledAt: "args:handledAt",
  								conversationId: "args:conversationId",
  								operation_Id: "args:operation_Id",
  								queryspan: "timespan:queryTimespan"
  							}
  						}
  					}
  				}
  			]
  		},
  		{
  			id: "errordetail",
  			width: "50%",
  			params: ["title","handledAt","type","operation_Id","queryspan"],
  			dataSources: [
  				{
  					id: "errordetail-data",
  					type: "ApplicationInsights/Query",
  					dependencies: { operation_Id: "dialog_errordetail:operation_Id",queryTimespan: "dialog_errordetail:queryspan" },
  					params: {
  						query: ({ operation_Id }) => ` 
                exceptions
                | where operation_Id == '${operation_Id}'
                | extend conversationId=customDimensions['Conversation ID']
                | project handledAt, type, innermostMessage, conversationId, operation_Id, timestamp, details `
  					}
  				}
  			],
  			elements: [
  				{
  					id: "errordetail-item",
  					type: "Detail",
  					title: "Error detail",
  					size: { w: 12,h: 16 },
  					dependencies: { values: "errordetail-data" },
  					props: {
  						cols: [
  							{ header: "Handle",field: "handledAt" },
  							{ header: "Type",field: "type" },
  							{ header: "Message",field: "innermostMessage" },
  							{ header: "Conversation ID",field: "conversationId" },
  							{ header: "Operation ID",field: "operation_Id" },
  							{ header: "Timestamp",field: "timestamp" },
  							{ header: "Details",field: "details" }
  						]
  					}
  				}
  			]
  		},
  		{
  			id: "userRetention",
  			width: "50%",
  			params: ["title","queryspan"],
  			dataSources: [
  				{
  					id: "ai-ur",
  					type: "ApplicationInsights/Query",
  					dependencies: {
  						timespan: "timespan",
  						queryTimespan: "timespan:queryTimespan",
  						granularity: "granularity",
  						selectedChannels: "filters:channels-values-selected",
  						selectedIntents: "filters:intents-values-selected"
  					},
  					params: {
  						table: "customEvents",
  						queries: {
  							retention_total_incoming_messages: {
  								query: () => `where name == 'MBFEvent.UserMessage' | count`,
  								filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }],
  								format: { type: "scorecard",args: { countField: "Count" } }
  							},
  							retention_avg_incoming_per_user: {
  								query: () => `
                    where name == 'MBFEvent.UserMessage' 
                    | extend userId=tostring(customDimensions.userId) 
                    | summarize messages=count() by userId 
                    | summarize count=avg(messages) `,
  								filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }],
  								format: { type: "scorecard" }
  							},
  							retention_total_outgoing_messages: {
  								query: () => `where name == 'MBFEvent.BotMessage' | count`,
  								filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }],
  								format: { type: "scorecard",args: { countField: "Count" } }
  							},
  							retention_avg_outgoing_per_user: {
  								query: () => `
                    where name == 'MBFEvent.BotMessage' 
                    | extend userId=tostring(customDimensions.userId), 
                      conversation=tostring(customDimensions.conversationId) 
                    | summarize messages=count() by userId, conversation 
                    | summarize count=avg(messages) `,
  								filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }],
  								format: { type: "scorecard" }
  							},
  							retention_avg_sessions_per_user: {
  								query: () => `
                    where name == 'MBFEvent.UserMessage' 
                    | extend userId=tostring(customDimensions.userId), 
                      conversation=tostring(customDimensions.conversationId) 
                    | summarize messages=count() by bin(timestamp, 1h), conversation, userId 
                    | summarize sum_sessions=count() by conversation, userId 
                    | summarize count=avg(sum_sessions)`,
  								filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }],
  								format: { type: "scorecard" }
  							},
  							retention_avg_messages_per_session: {
  								query: () => `
                    where name == 'MBFEvent.UserMessage' 
                    | extend userId=tostring(customDimensions.userId), 
                      conversation=tostring(customDimensions.conversationId) 
                    | summarize messages=count() by bin(timestamp, 1h), conversation, userId 
                    | summarize sum_sessions=count() by conversation, userId 
                    | summarize count=avg(sum_sessions)`,
  								filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }],
  								format: { type: "scorecard" }
  							},
  							retention_top_users: {
  								query: () => `
                    where name == 'MBFEvent.UserMessage' 
                    | extend userId=substring(tostring(customDimensions.userId), 0, 30), 
                      fullUserId=tostring(customDimensions.userId) 
                    | summarize messages=count() by fullUserId, userId 
                    | top 5 by messages 
                  `,
  								filters: [{ dependency: "selectedChannels",queryProperty: "customDimensions.channel" }]
  							}
  						}
  					}
  				}
  			],
  			elements: [
  				{
  					id: "retention-scores",
  					type: "Scorecard",
  					size: { w: 6,h: 3 },
  					dependencies: {
  						card_msgs_icon: "::chat",
  						card_msgs_value: "ai-ur:retention_total_incoming_messages-value",
  						card_msgs_heading: "::Total Msgs",
  						card_msgs_subvalue: "ai-ur:retention_total_outgoing_messages-value",
  						card_msgs_subheading: "::Outgoing",
  						card_msgs_color: "::#2196F3",
  						card_impu_icon: "::account_circle",
  						card_impu_value: "ai-ur:retention_avg_incoming_per_user-value",
  						card_impu_heading: "::Avg msg/usr",
  						card_impu_subvalue: "ai-ur:retention_avg_outgoing_per_user-value",
  						card_impu_subheading: "::Outgoing",
  						card_impu_color: "::#2196F3",
  						card_aspu_icon: "::account_circle",
  						card_aspu_value: "ai-ur:retention_avg_sessions_per_user-value",
  						card_aspu_heading: "::Avg ssn/usr",
  						card_aspu_subvalue: "ai-ur:retention_avg_messages_per_session-value",
  						card_aspu_subheading: "::Msg/Session",
  						card_aspu_color: "::#2196F3"
  					}
  				},
  				{
  					id: "user-retention-table",
  					type: "Table",
  					title: "User Retention",
  					size: { w: 3,h: 9 },
  					dependencies: { values: "retention" },
  					props: {
  						compact: true,
  						cols: [
  							{ header: "Time Span",field: "timespan" },
  							{ header: "Retention",field: "retention" },
  							{ header: "Returning",field: "returning" },
  							{ header: "Unique Users",field: "unique" }
  						]
  					}
  				},
  				{
  					id: "top-users-table",
  					type: "Table",
  					title: "Top Users",
  					size: { w: 3,h: 9 },
  					dependencies: { values: "ai-ur:retention_top_users" },
  					props: {
  						compact: true,
  						cols: [
  							{ header: "User Id",field: "userId" },
  							{ header: "Messages",field: "messages" },
  							{ type: "button",value: "chat",click: "openMessagesDialog" }
  						]
  					},
  					actions: {
  						openMessagesDialog: {
  							action: "dialog:userConversations",
  							params: { title: "args:userId",userId: "args:fullUserId",queryspan: "timespan:queryTimespan" }
  						}
  					}
  				}
  			]
  		},
  		{
  			id: "userConversations",
  			width: "60%",
  			params: ["title","userId","queryspan"],
  			dataSources: [
  				{
  					id: "user-conversations-data",
  					type: "ApplicationInsights/Query",
  					dependencies: { userId: "dialog_userConversations:userId",queryTimespan: "dialog_userConversations:queryspan" },
  					params: {
  						query: ({ userId }) => ` 
                customEvents
                | extend conversation=tostring(customDimensions.conversationId), userId=tostring(customDimensions.userId)
                | where name=='MBFEvent.UserMessage' and userId == '${userId}'
                | summarize count=count(), maxTimestamp=max(timestamp) by conversation
                | order by maxTimestamp`,
  						mappings: { id: (val, row, idx) => `Conversation ${idx}` }
  					}
  				}
  			],
  			elements: [
  				{
  					id: "user-conversations-list",
  					type: "Table",
  					title: "Conversations",
  					size: { w: 12,h: 16 },
  					dependencies: { values: "user-conversations-data" },
  					props: {
  						cols: [
  							{ header: "Conversation Id",field: "id" },
  							{ header: "Last Message",field: "maxTimestamp",type: "time",format: "MMM-DD HH:mm:ss" },
  							{ header: "Count",field: "count" },
  							{ type: "button",value: "chat",click: "openMessagesDialog" }
  						]
  					},
  					actions: {
  						openMessagesDialog: {
  							action: "dialog:messages",
  							params: {
  								title: "args:id",
  								conversation: "args:conversation",
  								intent: "dialog_intentConversations:intent",
  								queryspan: "timespan:queryTimespan"
  							}
  						}
  					}
  				}
  			]
  		}
  	]
  }