diff --git a/images/X-errorMessage.svg b/images/X-errorMessage.svg new file mode 100644 index 000000000..17dd393c6 --- /dev/null +++ b/images/X-errorMessage.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Explorer/QueryCopilot/QueryCopilotTab.tsx b/src/Explorer/QueryCopilot/QueryCopilotTab.tsx index 74c15d653..002e485de 100644 --- a/src/Explorer/QueryCopilot/QueryCopilotTab.tsx +++ b/src/Explorer/QueryCopilot/QueryCopilotTab.tsx @@ -47,9 +47,15 @@ import HintIcon from "../../../images/Hint.svg"; import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg"; import RecentIcon from "../../../images/Recent.svg"; import SamplePromptsIcon from "../../../images/SamplePromptsIcon.svg"; +import XErrorMessage from "../../../images/X-errorMessage.svg"; import SaveQueryIcon from "../../../images/save-cosmos.svg"; import { useTabs } from "../../hooks/useTabs"; +interface SuggestedPrompt { + id: number; + text: string; +} + interface QueryCopilotTabProps { initialInput: string; explorer: Explorer; @@ -89,6 +95,7 @@ export const QueryCopilotTab: React.FC = ({ const [showDeletePopup, setShowDeletePopup] = useState(false); const [showFeedbackBar, setShowFeedbackBar] = useState(false); const [showCopyPopup, setshowCopyPopup] = useState(false); + const [showErrorMessageBar, setShowErrorMessageBar] = useState(false); const sampleProps: SamplePromptsProps = { isSamplePromptsOpen: isSamplePromptsOpen, @@ -116,16 +123,40 @@ export const QueryCopilotTab: React.FC = ({ const cachedHistoriesString = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotHistories`); const cachedHistories = cachedHistoriesString?.split(","); const [histories, setHistories] = useState(cachedHistories || []); + const suggestedPrompts: SuggestedPrompt[] = [ + { id: 1, text: "Give me all customers whose names start with C" }, + { id: 2, text: "Show me all customers" }, + { id: 3, text: "Show me all customers who bought a bike in 2019" }, + ]; + const [filteredHistories, setFilteredHistories] = useState(histories); + const [filteredSuggestedPrompts, setFilteredSuggestedPrompts] = useState(suggestedPrompts); + + const handleUserPromptChange = (event: React.ChangeEvent) => { + const { value } = event.target; + setUserPrompt(value); + + // Filter history prompts + const filteredHistory = histories.filter((history) => history.toLowerCase().includes(value.toLowerCase())); + setFilteredHistories(filteredHistory); + + // Filter suggested prompts + const filteredSuggested = suggestedPrompts.filter((prompt) => + prompt.text.toLowerCase().includes(value.toLowerCase()) + ); + setFilteredSuggestedPrompts(filteredSuggested); + }; const updateHistories = (): void => { const newHistories = histories.length < 3 ? [userPrompt, ...histories] : [userPrompt, histories[1], histories[2]]; setHistories(newHistories); localStorage.setItem(`${userContext.databaseAccount.id}-queryCopilotHistories`, newHistories.join(",")); }; + const generateSQLQuery = async (): Promise => { try { setIsGeneratingQuery(true); useTabs.getState().setIsTabExecuting(true); + useTabs.getState().setIsQueryErrorThrown(false); const payload = { containerSchema: QueryCopilotSampleContainerSchema, userPrompt: userPrompt, @@ -151,6 +182,8 @@ export const QueryCopilotTab: React.FC = ({ } } catch (error) { handleError(error, "executeNaturalLanguageQuery"); + useTabs.getState().setIsQueryErrorThrown(true); + setShowErrorMessageBar(true); throw error; } finally { setIsGeneratingQuery(false); @@ -175,6 +208,7 @@ export const QueryCopilotTab: React.FC = ({ try { setIsExecuting(true); useTabs.getState().setIsTabExecuting(true); + useTabs.getState().setIsQueryErrorThrown(false); const queryResults: QueryResults = await queryPagesUntilContentPresent( firstItemIndex, async (firstItemIndex: number) => @@ -187,6 +221,8 @@ export const QueryCopilotTab: React.FC = ({ const errorMessage = getErrorMessage(error); setErrorMessage(errorMessage); handleError(errorMessage, "executeQueryCopilotTab"); + useTabs.getState().setIsQueryErrorThrown(true); + setShowErrorMessageBar(true); } finally { setIsExecuting(false); useTabs.getState().setIsTabExecuting(false); @@ -236,19 +272,20 @@ export const QueryCopilotTab: React.FC = ({ Copilot - + setUserPrompt(newValue)} + onChange={handleUserPromptChange} style={{ lineHeight: 30 }} styles={{ root: { width: "95%" } }} disabled={isGeneratingQuery} + autoComplete="off" onClick={() => setShowSamplePrompts(true)} /> { updateHistories(); @@ -259,14 +296,16 @@ export const QueryCopilotTab: React.FC = ({ {showSamplePrompts && ( setShowSamplePrompts(false)} + directionalHintFixed={true} directionalHint={DirectionalHint.bottomLeftEdge} + alignTargetEdge={true} + gapSpace={4} > - {histories?.length > 0 && ( + {filteredHistories?.length > 0 && ( = ({ > Recent - {histories.map((history, i) => ( + {filteredHistories.map((history, i) => ( { @@ -307,37 +346,27 @@ export const QueryCopilotTab: React.FC = ({ > Suggested Prompts - { - setUserPrompt("Give me all customers whose names start with C"); - setShowSamplePrompts(false); + {filteredSuggestedPrompts.map((prompt) => ( + { + setUserPrompt(prompt.text); + setShowSamplePrompts(false); + }} + onRenderIcon={() => } + styles={promptStyles} + > + {prompt.text} + + ))} + } - styles={promptStyles} - > - Give me all customers whose names start with C - - { - setUserPrompt("Show me all customers"); - setShowSamplePrompts(false); - }} - onRenderIcon={() => } - styles={promptStyles} - > - Show me all customers - - { - setUserPrompt("Show me all customers who bought a bike in 2019"); - setShowSamplePrompts(false); - }} - onRenderIcon={() => } - styles={promptStyles} - > - Show me all customers who bought a bike in 2019 - - + /> = ({ )} + AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "} Read preview terms + {showErrorMessageBar ? ( + + + + We ran into an error and were not able to execute query. Please try again after sometime + + + ) : ( + <> + )} {showFeedbackBar ? ( diff --git a/src/Explorer/QueryCopilot/__snapshots__/QueryCopilotTab.test.tsx.snap b/src/Explorer/QueryCopilot/__snapshots__/QueryCopilotTab.test.tsx.snap index 1b871c3f6..95b93c41f 100644 --- a/src/Explorer/QueryCopilot/__snapshots__/QueryCopilotTab.test.tsx.snap +++ b/src/Explorer/QueryCopilot/__snapshots__/QueryCopilotTab.test.tsx.snap @@ -35,12 +35,14 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] = style={ Object { "marginTop": 16, + "position": "relative", "width": "100%", } } verticalAlign="center" > )} + {isQueryErrorThrown(tab, tabKind) && ( + Error + )} {useObservable(tab?.tabTitle || getReactTabTitle())} {tabKind !== ReactTabKind.Home && ( @@ -220,6 +229,19 @@ const isTabExecuting = (tab?: Tab, tabKind?: ReactTabKind): boolean => { return false; }; +const isQueryErrorThrown = (tab?: Tab, tabKind?: ReactTabKind): boolean => { + if ( + !tab?.isExecuting && + tabKind !== undefined && + tabKind !== ReactTabKind.Home && + useTabs.getState()?.isQueryErrorThrown && + !useTabs.getState()?.isTabExecuting + ) { + return true; + } + return false; +}; + const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => { switch (activeReactTab) { case ReactTabKind.Connect: diff --git a/src/hooks/useTabs.ts b/src/hooks/useTabs.ts index 7159b0f34..73c812b79 100644 --- a/src/hooks/useTabs.ts +++ b/src/hooks/useTabs.ts @@ -12,6 +12,7 @@ interface TabsState { networkSettingsWarning: string; queryCopilotTabInitialInput: string; isTabExecuting: boolean; + isQueryErrorThrown: boolean; activateTab: (tab: TabsBase) => void; activateNewTab: (tab: TabsBase) => void; activateReactTab: (tabkind: ReactTabKind) => void; @@ -26,6 +27,7 @@ interface TabsState { setNetworkSettingsWarning: (warningMessage: string) => void; setQueryCopilotTabInitialInput: (input: string) => void; setIsTabExecuting: (state: boolean) => void; + setIsQueryErrorThrown: (state: boolean) => void; } export enum ReactTabKind { @@ -43,6 +45,7 @@ export const useTabs: UseStore = create((set, get) => ({ networkSettingsWarning: "", queryCopilotTabInitialInput: "", isTabExecuting: false, + isQueryErrorThrown: false, activateTab: (tab: TabsBase): void => { if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) { set({ activeTab: tab, activeReactTab: undefined }); @@ -157,4 +160,7 @@ export const useTabs: UseStore = create((set, get) => ({ setIsTabExecuting: (state: boolean) => { set({ isTabExecuting: state }); }, + setIsQueryErrorThrown: (state: boolean) => { + set({ isQueryErrorThrown: state }); + }, }));