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