Skip to content

Commit 37fec50

Browse files
Enhance AI Assistant with seeded questions and UI improvements
- Added seeded questions for quick access to common queries. - Improved layout and styling for header controls and question buttons. - Updated message sending logic to support seeded questions. - Refined CSS for better spacing and alignment in the AI Assistant component.
1 parent fecbc8f commit 37fec50

File tree

3 files changed

+134
-16
lines changed

3 files changed

+134
-16
lines changed

src/assets/DemoScript.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This walkthrough showcases both Analyst capabilities (Mario) and Business User f
2727
| Digging deeper, she finds a specific battery type is driving returns. | Scroll to the Pulse metric → Click **Breakdown** → Filter by **Battery Type** | Notice how filtering by dimensions reveals clear root causes—in this case, a problematic battery type. |
2828
| She wants to confirm whether this battery type also impacts sales. | Click on question Q1 (auto-seeded) | This uses **Pulse Enhanced Q&A** to correlate across multiple metrics. If no answer returns, move on—Q2 offers another pre-scripted question, or type your own. |
2929
| The Pulse analysis was great, but she wants to extend her search using conversational AI. | Click [AI Assistent](/McKenzie/ai-assistant) in the header | This uses **Tableau MCP Server** to allow the user to ask any question. The MCP Server sends a system prompt with the user question to the LLM, that has now access to the tools provided by the MCP Server. More specically, the LLM can now list the data sources in the site, find the fields in the data sources that can answer the user question, and query the actual data source. |
30+
| Next, she wants to do some analysis on publicly accessible data to learn about the impact of accidents to inform here customers | Type in "Using the data source "Incidents, Accidents, & Occupational Safety", can you write an analytical summary of the impact of weather and location on the number of accidents" | This will use another data source (because the system template will use the e-bikes data source by default), and makes correlations using the data it can query |
3031
| She wants to know the number of returns by battery type. | Type in "Show the number of returns by battery type by year" | You will see in the progress window that the LLM is using multiple iterations, and even correcting itself when errors occur, and using the different tools provided by MCP server to come up with the answer. The visualization in the bottom is using an open source charting library (rechart) to visualize the response of the LLM. |
3132
| Concerned, McKenzie calls Mario to report the issue. | Click the profile icon (top right) ([jump to](/)) | Return to the login screen. |
3233
| Mario wants to run his own analysis before taking action. | Click **Analyze** in the header ([jump to](/Mario/Analyze)) | A blank workbook opens, connected to the same published data source. Full Tableau authoring experience. |

src/client/components/analytics/AIAssistent.module.css

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@
3232
font-size: 14px;
3333
}
3434

35-
.clearButton {
35+
.headerControls {
36+
display: flex;
37+
flex-direction: column;
3638
margin-top: 10px;
39+
gap: 10px;
40+
}
41+
42+
.clearButton {
3743
padding: 6px 12px;
3844
background: #dc3545;
3945
color: white;
@@ -42,12 +48,43 @@
4248
cursor: pointer;
4349
font-size: 14px;
4450
transition: background-color 0.2s ease;
51+
width: 150px
4552
}
4653

4754
.clearButton:hover {
4855
background: #c82333;
4956
}
5057

58+
.seededQuestions {
59+
display: flex;
60+
align-items: center;
61+
gap: 8px;
62+
flex-wrap: wrap;
63+
}
64+
65+
.seededQuestionsLabel {
66+
font-size: 14px;
67+
color: #6c757d;
68+
font-weight: 500;
69+
}
70+
71+
.seededQuestionButton {
72+
padding: 4px 8px;
73+
background: #007bff;
74+
color: white;
75+
border: none;
76+
border-radius: 4px;
77+
cursor: pointer;
78+
font-size: 12px;
79+
font-weight: 500;
80+
transition: background-color 0.2s ease;
81+
min-width: 32px;
82+
}
83+
84+
.seededQuestionButton:hover {
85+
background: #0056b3;
86+
}
87+
5188
.chatContainer {
5289
flex: 1;
5390
display: flex;
@@ -491,10 +528,30 @@
491528
font-size: 13px;
492529
}
493530

531+
.headerControls {
532+
flex-direction: column;
533+
align-items: flex-start;
534+
gap: 8px;
535+
margin-top: 8px;
536+
}
537+
494538
.clearButton {
495539
padding: 8px 12px;
496540
font-size: 13px;
497-
margin-top: 8px;
541+
}
542+
543+
.seededQuestions {
544+
gap: 6px;
545+
}
546+
547+
.seededQuestionsLabel {
548+
font-size: 13px;
549+
}
550+
551+
.seededQuestionButton {
552+
padding: 4px 6px;
553+
font-size: 11px;
554+
min-width: 28px;
498555
}
499556

500557
.messagesContainer {
@@ -645,10 +702,30 @@
645702
font-size: 12px;
646703
}
647704

705+
.headerControls {
706+
flex-direction: column;
707+
align-items: flex-start;
708+
gap: 6px;
709+
margin-top: 6px;
710+
}
711+
648712
.clearButton {
649713
padding: 6px 10px;
650714
font-size: 12px;
651-
margin-top: 6px;
715+
}
716+
717+
.seededQuestions {
718+
gap: 4px;
719+
}
720+
721+
.seededQuestionsLabel {
722+
font-size: 12px;
723+
}
724+
725+
.seededQuestionButton {
726+
padding: 3px 5px;
727+
font-size: 10px;
728+
min-width: 26px;
652729
}
653730

654731
.messagesContainer {

src/client/components/analytics/AIAssistent.tsx

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,12 @@ function AIAssistent() {
211211
}
212212
}, [progressUpdates]);
213213

214-
const sendMessage = async () => {
215-
if (!currentQuery.trim() || isLoading) return;
214+
const sendMessage = async (query: string) => {
215+
if (!query.trim() || isLoading) return;
216216

217217
const userMessage: ChatMessage = {
218218
role: 'user',
219-
content: currentQuery.trim(),
219+
content: query.trim(),
220220
timestamp: new Date(),
221221
};
222222

@@ -330,7 +330,7 @@ function AIAssistent() {
330330
const handleKeyPress = (e: React.KeyboardEvent) => {
331331
if (e.key === 'Enter' && !e.shiftKey) {
332332
e.preventDefault();
333-
sendMessage();
333+
sendMessage(currentQuery);
334334
}
335335
};
336336

@@ -340,6 +340,18 @@ function AIAssistent() {
340340
setError(null);
341341
};
342342

343+
const handleSeededQuestion = (questionNumber: number) => {
344+
const seededQuestions = {
345+
1: "Show me the sales by year?",
346+
2: "How is the bike sales KPI trending?",
347+
3: "What data sources are available and what are the interesting fields in each data source?",
348+
4: "Using the data source 'Incidents, Accidents, & Occupational Safety', can you write an analytical summary of the impact of weather and location on the number of accidents",
349+
};
350+
351+
const query = seededQuestions[questionNumber as keyof typeof seededQuestions] || '';
352+
sendMessage(query);
353+
};
354+
343355
const formatTimestamp = (date: Date) => {
344356
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
345357
};
@@ -532,17 +544,46 @@ function AIAssistent() {
532544
<div className={styles.root}>
533545
<div className={styles.header}>
534546
<h1 className={styles.title}>Tableau AI Assistant</h1>
547+
<div className={styles.headerControls}>
535548
<p className={styles.subtitle}>
536549
Ask questions about your data, dashboards, and analytics. Powered by Tableau's MCP.
537550
</p>
551+
<div className={styles.seededQuestions}>
552+
<span className={styles.seededQuestionsLabel}>Seeded questions:</span>
553+
<button
554+
onClick={() => handleSeededQuestion(1)}
555+
className={styles.seededQuestionButton}
556+
>
557+
Data Question
558+
</button>
559+
<button
560+
onClick={() => handleSeededQuestion(2)}
561+
className={styles.seededQuestionButton}
562+
>
563+
Pulse
564+
</button>
565+
<button
566+
onClick={() => handleSeededQuestion(3)}
567+
className={styles.seededQuestionButton}
568+
>
569+
Datasource Info
570+
</button>
571+
<button
572+
onClick={() => handleSeededQuestion(4)}
573+
className={styles.seededQuestionButton}
574+
>
575+
Summary using different data source
576+
</button>
577+
</div>
538578
{messages.length > 0 && (
539-
<button
540-
onClick={clearChat}
541-
className={styles.clearButton}
542-
>
543-
Clear Chat
544-
</button>
545-
)}
579+
<button
580+
onClick={clearChat}
581+
className={styles.clearButton}
582+
>
583+
Clear Chat
584+
</button>
585+
)}
586+
</div>
546587
</div>
547588

548589
<div className={styles.chatContainer}>
@@ -560,7 +601,6 @@ function AIAssistent() {
560601
{messages.map((message, index) => {
561602
const currentToggles = messageToggles[index] || { showTools: false, showCharts: false };
562603
const hasVegaSpecs = message.role === 'assistant' && hasVegaLiteSpecs(message);
563-
const shouldShowCharts = currentToggles.showCharts || hasVegaSpecs;
564604

565605
return (
566606
<div
@@ -675,7 +715,7 @@ function AIAssistent() {
675715
/>
676716
<button
677717
className={styles.sendButton}
678-
onClick={sendMessage}
718+
onClick={() => sendMessage(currentQuery)}
679719
disabled={!currentQuery.trim() || isLoading}
680720
>
681721
{isLoading ? 'Sending...' : 'Send'}

0 commit comments

Comments
 (0)