diff --git a/src/plugins/visualize/public/application/components/query_assistant_logo.svg b/src/plugins/visualize/public/application/components/query_assistant_logo.svg
new file mode 100644
index 000000000000..d21737d8224f
--- /dev/null
+++ b/src/plugins/visualize/public/application/components/query_assistant_logo.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx
index d0a1755f275e..27ad2fe59c06 100644
--- a/src/plugins/visualize/public/application/components/visualize_top_nav.tsx
+++ b/src/plugins/visualize/public/application/components/visualize_top_nav.tsx
@@ -28,10 +28,20 @@
* under the License.
*/
-import React, { memo, useCallback, useMemo, useState, useEffect } from 'react';
+import React, { memo, useCallback, useMemo, useState, useEffect, useRef } from 'react';
import { AppMountParameters, OverlayRef } from 'opensearch-dashboards/public';
import { i18n } from '@osd/i18n';
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiFieldText,
+ EuiButton,
+ EuiIcon,
+ EuiInputPopover,
+ EuiListGroup,
+ EuiListGroupItem,
+} from '@elastic/eui';
import { useOpenSearchDashboards } from '../../../../opensearch_dashboards_react/public';
import {
VisualizeServices,
@@ -42,6 +52,7 @@ import {
import { APP_NAME } from '../visualize_constants';
import { getTopNavConfig } from '../utils';
import type { IndexPattern } from '../../../../data/public';
+import chatLogo from './query_assistant_logo.svg';
interface VisualizeTopNavProps {
currentAppState: VisualizeAppState;
@@ -57,6 +68,7 @@ interface VisualizeTopNavProps {
visualizationIdFromUrl?: string;
embeddableId?: string;
onAppLeave: AppMountParameters['onAppLeave'];
+ onPPL?: (ppl: string) => void;
}
const TopNav = ({
@@ -73,6 +85,7 @@ const TopNav = ({
visualizationIdFromUrl,
embeddableId,
onAppLeave,
+ onPPL,
}: VisualizeTopNavProps) => {
const { services } = useOpenSearchDashboards();
const { TopNavMenu } = services.navigation.ui;
@@ -199,6 +212,34 @@ const TopNav = ({
asyncSetIndexPattern();
}
}, [vis.params, vis.type, services.data.indexPatterns, vis.data.indexPattern]);
+ const [value, setValue] = useState('');
+ const [generating, setGenerating] = useState(false);
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+ const inputRef = useRef(null);
+
+ const HARDCODED_SUGGESTIONS: string[] = [
+ `what's the revenue for past week and group by day?`,
+ 'how many orders per day for past week?',
+ ];
+
+ const isVega = vis.type.name === 'vega';
+
+ const indexName = 'opensearch_dashboards_sample_data_ecommerce';
+ const onGenerate = async () => {
+ setGenerating(true);
+ const pplResponse = await services.http.post('/api/observability/query_assist/generate_ppl', {
+ body: JSON.stringify({
+ question: value,
+ index: indexName,
+ }),
+ });
+ // eslint-disable-next-line no-console
+ console.log(pplResponse);
+ setGenerating(false);
+ if (onPPL) {
+ onPPL(pplResponse);
+ }
+ };
return isChromeVisible ? (
/**
@@ -208,23 +249,76 @@ const TopNav = ({
* All visualizations also have the timepicker\autorefresh component,
* it is enabled by default in the TopNavMenu component.
*/
-
+ <>
+ {isVega ? (
+
+
+ }
+ fullWidth
+ onChange={(event) => setValue(event.target.value)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') onGenerate();
+ }}
+ />
+ }
+ disableFocusTrap
+ fullWidth={true}
+ isOpen={isPopoverOpen}
+ closePopover={() => {
+ setIsPopoverOpen(false);
+ }}
+ >
+
+ {HARDCODED_SUGGESTIONS?.map((question) => (
+ {
+ setValue(question);
+ inputRef.current?.focus();
+ setIsPopoverOpen(false);
+ }}
+ label={question}
+ />
+ ))}
+
+
+
+
+ onGenerate()} isLoading={generating}>
+ {generating ? 'Generating' : 'Generate'}
+
+
+
+ ) : (
+
+ )}
+ >
) : showFilterBar ? (
/**
* The top nav is hidden in embed mode, but the filter bar must still be present so