Redesign copilot (#1650)
* Add copilot toggle button * take out toggle button when closing tab * Update snapshots * Fix the prettier issue * fix prettier
This commit is contained in:
parent
44d6f29edd
commit
e42e24b175
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M13.3626 11.9771C13.1573 11.9837 13.019 12.0288 12.9215 12.0836C12.817 12.1424 12.7301 12.229 12.6508 12.3542C12.4802 12.6239 12.3837 12.9957 12.252 13.5033L12.2347 13.5699C12.1079 14.0581 11.9424 14.6764 11.5775 15.1594C11.1788 15.687 10.5903 16 9.74835 16H4.63681C3.55423 16 2.75741 15.2435 2.18373 14.3271C1.60629 13.4047 1.17786 12.2045 0.845481 11.0542L0.845003 11.0525C0.464365 9.73512 -0.017355 8.06787 0.000479698 6.72244C0.00951195 6.04103 0.145994 5.34953 0.569528 4.825C1.00712 4.28305 1.66518 4.02294 2.50365 4.02294H2.63739C2.84267 4.01631 2.98102 3.97123 3.07848 3.9164C3.18305 3.85757 3.26995 3.77102 3.34916 3.64584C3.51985 3.37613 3.61631 3.00432 3.74801 2.49671L3.76529 2.43012C3.89213 1.94191 4.05757 1.32365 4.42251 0.840633C4.82119 0.312966 5.40972 0 6.25165 0H11.3632C12.4458 0 13.2426 0.756543 13.8163 1.67295C14.3937 2.59534 14.8221 3.79546 15.1545 4.94582L15.155 4.94748C15.5356 6.26488 16.0174 7.93213 15.9995 9.27756C15.9905 9.95897 15.854 10.6505 15.4305 11.175C14.9929 11.717 14.3348 11.9771 13.4963 11.9771H13.3626ZM14.1354 5.28381C13.8114 4.16251 13.4194 3.09096 12.9303 2.3096C12.4374 1.52225 11.9215 1.14296 11.3632 1.14296H7.95613C8.10274 1.42082 8.22537 1.731 8.33548 2.05199C8.48008 2.47355 8.61427 2.94842 8.75233 3.43699L8.78302 3.54557C9.32758 5.47082 10.008 7.94794 10.4498 9.5646C10.6507 10.2998 11.2693 10.8098 11.9798 10.8333H13.4156C13.4253 10.8333 13.4351 10.8335 13.4447 10.8341H13.4963C14.1298 10.8341 14.4484 10.6443 14.6237 10.4272C14.813 10.1928 14.9255 9.8162 14.9328 9.26133C14.9477 8.13479 14.5323 6.65749 14.1354 5.28381ZM3.06972 13.6904C3.56261 14.4777 4.0785 14.857 4.63681 14.857H8.04387C7.89726 14.5792 7.77463 14.269 7.66452 13.948C7.51992 13.5265 7.38573 13.0516 7.24767 12.563L7.21698 12.4544C6.67242 10.5292 5.99197 8.05206 5.55019 6.4354C5.34929 5.70024 4.73069 5.19015 4.02018 5.16674H2.58445C2.57465 5.16674 2.56492 5.16646 2.55526 5.1659H2.50365C1.87025 5.1659 1.5516 5.35572 1.37634 5.57277C1.18703 5.80723 1.07454 6.1838 1.06719 6.73867C1.05225 7.86521 1.46769 9.34251 1.86459 10.7162C2.18857 11.8375 2.58057 12.909 3.06972 13.6904ZM9.10298 10.8333H9.87304C9.67506 10.5554 9.5217 10.236 9.42598 9.88575C9.233 9.17956 8.99466 8.30985 8.74215 7.39421L8.47331 6.42622C8.26579 5.67902 7.62465 5.16674 6.89702 5.16674H6.12695C6.32494 5.44463 6.4783 5.76395 6.57402 6.11425C6.767 6.82044 7.00534 7.69015 7.25785 8.60579L7.52669 9.57378C7.73421 10.321 8.37535 10.8333 9.10298 10.8333ZM6.89702 4.02378C7.23212 4.02378 7.55609 4.08968 7.85646 4.21152C7.82459 4.0984 7.7931 3.98682 7.76205 3.87706L7.73417 3.77844C7.59304 3.27914 7.46754 2.83514 7.33414 2.44626C7.19129 2.02979 7.05211 1.71602 6.90214 1.4942C6.77014 1.29897 6.56087 1.14296 6.25165 1.14296C5.69009 1.14296 5.42306 1.33297 5.2517 1.55976C5.04661 1.83121 4.92755 2.21889 4.79304 2.73662C4.78284 2.77588 4.77249 2.81622 4.76191 2.85744C4.66966 3.21703 4.55997 3.64464 4.37733 4.02378H6.89702ZM9.10298 11.9762C8.76788 11.9762 8.44391 11.9103 8.14354 11.7885C8.1754 11.9016 8.2069 12.0132 8.23795 12.1229L8.26583 12.2216C8.40696 12.7208 8.53246 13.1649 8.66585 13.5537C8.80871 13.9702 8.94789 14.284 9.09786 14.5058C9.22986 14.701 9.43913 14.857 9.74835 14.857C10.3099 14.857 10.5769 14.667 10.7483 14.4402C10.9534 14.1688 11.0725 13.7811 11.207 13.2634C11.2172 13.2241 11.2275 13.1838 11.2381 13.1426C11.3303 12.783 11.44 12.3554 11.6227 11.9762H9.10298Z" fill="#0078D4"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.4 KiB |
|
@ -52,7 +52,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack horizontalAlign="center">
|
<Stack horizontalAlign="center">
|
||||||
<Stack.Item align="center" style={{ textAlign: "center" }}>
|
<Stack.Item align="center" style={{ textAlign: "center" }}>
|
||||||
<Text className="title bold">Welcome to Query Copilot for Azure Cosmos DB (Private Preview)</Text>
|
<Text className="title bold">Welcome to Copilot in Azure Cosmos DB (Private Preview)</Text>
|
||||||
</Stack.Item>
|
</Stack.Item>
|
||||||
<Stack.Item align="center" className="text">
|
<Stack.Item align="center" className="text">
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
|
@ -61,7 +61,7 @@ export const WelcomeModal = ({ visible }: { visible: boolean }): JSX.Element =>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem align="start">
|
<StackItem align="start">
|
||||||
<Text className="bold">
|
<Text className="bold">
|
||||||
Let Query Copilot do the work for you
|
Let Copilot do the work for you
|
||||||
<br />
|
<br />
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
|
|
|
@ -76,7 +76,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||||
<Text
|
<Text
|
||||||
className="title bold"
|
className="title bold"
|
||||||
>
|
>
|
||||||
Welcome to Query Copilot for Azure Cosmos DB (Private Preview)
|
Welcome to Copilot in Azure Cosmos DB (Private Preview)
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
<StackItem
|
<StackItem
|
||||||
|
@ -100,7 +100,7 @@ exports[`Query Copilot Welcome Modal snapshot test should render when isOpen is
|
||||||
<Text
|
<Text
|
||||||
className="bold"
|
className="bold"
|
||||||
>
|
>
|
||||||
Let Query Copilot do the work for you
|
Let Copilot do the work for you
|
||||||
<br />
|
<br />
|
||||||
</Text>
|
</Text>
|
||||||
</StackItem>
|
</StackItem>
|
||||||
|
|
|
@ -0,0 +1,563 @@
|
||||||
|
import {
|
||||||
|
Callout,
|
||||||
|
CommandBarButton,
|
||||||
|
DefaultButton,
|
||||||
|
DirectionalHint,
|
||||||
|
IButtonStyles,
|
||||||
|
IconButton,
|
||||||
|
Image,
|
||||||
|
Link,
|
||||||
|
MessageBar,
|
||||||
|
MessageBarType,
|
||||||
|
Separator,
|
||||||
|
Spinner,
|
||||||
|
Stack,
|
||||||
|
TeachingBubble,
|
||||||
|
Text,
|
||||||
|
TextField,
|
||||||
|
} from "@fluentui/react";
|
||||||
|
import { useBoolean } from "@fluentui/react-hooks";
|
||||||
|
import {
|
||||||
|
ContainerStatusType,
|
||||||
|
PoolIdType,
|
||||||
|
QueryCopilotSampleContainerSchema,
|
||||||
|
ShortenedQueryCopilotSampleContainerSchema,
|
||||||
|
} from "Common/Constants";
|
||||||
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
|
import { createUri } from "Common/UrlUtility";
|
||||||
|
import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
|
||||||
|
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
|
||||||
|
import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
|
||||||
|
import { SubmitFeedback } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||||
|
import { GenerateSQLQueryResponse, QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||||
|
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/Shared/SamplePrompts/SamplePrompts";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
|
import React, { useRef, useState } from "react";
|
||||||
|
import HintIcon from "../../../images/Hint.svg";
|
||||||
|
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
|
||||||
|
import RecentIcon from "../../../images/Recent.svg";
|
||||||
|
import errorIcon from "../../../images/close-black.svg";
|
||||||
|
import { useTabs } from "../../hooks/useTabs";
|
||||||
|
|
||||||
|
type QueryCopilotPromptProps = QueryCopilotProps & {
|
||||||
|
toggleCopilot: (toggle: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface SuggestedPrompt {
|
||||||
|
id: number;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const promptStyles: IButtonStyles = {
|
||||||
|
root: { border: 0, selectors: { ":hover": { outline: "1px dashed #605e5c" } } },
|
||||||
|
label: { fontWeight: 400, textAlign: "left", paddingLeft: 8 },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
||||||
|
explorer,
|
||||||
|
toggleCopilot,
|
||||||
|
}: QueryCopilotPromptProps): JSX.Element => {
|
||||||
|
const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
|
||||||
|
const inputEdited = useRef(false);
|
||||||
|
const {
|
||||||
|
hideFeedbackModalForLikedQueries,
|
||||||
|
userPrompt,
|
||||||
|
setUserPrompt,
|
||||||
|
generatedQuery,
|
||||||
|
setGeneratedQuery,
|
||||||
|
query,
|
||||||
|
setQuery,
|
||||||
|
isGeneratingQuery,
|
||||||
|
setIsGeneratingQuery,
|
||||||
|
likeQuery,
|
||||||
|
setLikeQuery,
|
||||||
|
dislikeQuery,
|
||||||
|
setDislikeQuery,
|
||||||
|
showCallout,
|
||||||
|
setShowCallout,
|
||||||
|
isSamplePromptsOpen,
|
||||||
|
setIsSamplePromptsOpen,
|
||||||
|
showSamplePrompts,
|
||||||
|
setShowSamplePrompts,
|
||||||
|
showDeletePopup,
|
||||||
|
setShowDeletePopup,
|
||||||
|
showFeedbackBar,
|
||||||
|
setShowFeedbackBar,
|
||||||
|
showCopyPopup,
|
||||||
|
setshowCopyPopup,
|
||||||
|
showErrorMessageBar,
|
||||||
|
showInvalidQueryMessageBar,
|
||||||
|
setShowInvalidQueryMessageBar,
|
||||||
|
setShowErrorMessageBar,
|
||||||
|
setGeneratedQueryComments,
|
||||||
|
setQueryResults,
|
||||||
|
setErrorMessage,
|
||||||
|
} = useQueryCopilot();
|
||||||
|
|
||||||
|
const sampleProps: SamplePromptsProps = {
|
||||||
|
isSamplePromptsOpen: isSamplePromptsOpen,
|
||||||
|
setIsSamplePromptsOpen: setIsSamplePromptsOpen,
|
||||||
|
setTextBox: setUserPrompt,
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyGeneratedCode = () => {
|
||||||
|
if (!query) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const queryElement = document.createElement("textarea");
|
||||||
|
queryElement.value = query;
|
||||||
|
document.body.appendChild(queryElement);
|
||||||
|
queryElement.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
document.body.removeChild(queryElement);
|
||||||
|
|
||||||
|
setshowCopyPopup(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setshowCopyPopup(false);
|
||||||
|
}, 6000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cachedHistoriesString = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotHistories`);
|
||||||
|
const cachedHistories = cachedHistoriesString?.split("|");
|
||||||
|
const [histories, setHistories] = useState<string[]>(cachedHistories || []);
|
||||||
|
const suggestedPrompts: SuggestedPrompt[] = [
|
||||||
|
{ id: 1, text: 'Show all products that have the word "ultra" in the name or description' },
|
||||||
|
{ id: 2, text: "What are all of the possible categories for the products, and their counts?" },
|
||||||
|
{ id: 3, text: 'Show me all products that have been reviewed by someone with a username that contains "bob"' },
|
||||||
|
];
|
||||||
|
const [filteredHistories, setFilteredHistories] = useState<string[]>(histories);
|
||||||
|
const [filteredSuggestedPrompts, setFilteredSuggestedPrompts] = useState<SuggestedPrompt[]>(suggestedPrompts);
|
||||||
|
|
||||||
|
const handleUserPromptChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
inputEdited.current = true;
|
||||||
|
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 formattedUserPrompt = userPrompt.replace(/\s+/g, " ").trim();
|
||||||
|
const existingHistories = histories.map((history) => history.replace(/\s+/g, " ").trim());
|
||||||
|
|
||||||
|
const updatedHistories = existingHistories.filter(
|
||||||
|
(history) => history.toLowerCase() !== formattedUserPrompt.toLowerCase(),
|
||||||
|
);
|
||||||
|
const newHistories = [formattedUserPrompt, ...updatedHistories.slice(0, 2)];
|
||||||
|
|
||||||
|
setHistories(newHistories);
|
||||||
|
localStorage.setItem(`${userContext.databaseAccount.id}-queryCopilotHistories`, newHistories.join("|"));
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetMessageStates = (): void => {
|
||||||
|
setShowErrorMessageBar(false);
|
||||||
|
setShowInvalidQueryMessageBar(false);
|
||||||
|
setShowFeedbackBar(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetQueryResults = (): void => {
|
||||||
|
setQueryResults(null);
|
||||||
|
setErrorMessage("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateSQLQuery = async (): Promise<void> => {
|
||||||
|
try {
|
||||||
|
resetMessageStates();
|
||||||
|
setIsGeneratingQuery(true);
|
||||||
|
setShowDeletePopup(false);
|
||||||
|
useTabs.getState().setIsTabExecuting(true);
|
||||||
|
useTabs.getState().setIsQueryErrorThrown(false);
|
||||||
|
if (
|
||||||
|
useQueryCopilot.getState().containerStatus.status !== ContainerStatusType.Active &&
|
||||||
|
!userContext.features.disableCopilotPhoenixGateaway
|
||||||
|
) {
|
||||||
|
await explorer.allocateContainer(PoolIdType.QueryCopilot);
|
||||||
|
}
|
||||||
|
const payload = {
|
||||||
|
containerSchema: userContext.features.enableCopilotFullSchema
|
||||||
|
? QueryCopilotSampleContainerSchema
|
||||||
|
: ShortenedQueryCopilotSampleContainerSchema,
|
||||||
|
userPrompt: userPrompt,
|
||||||
|
};
|
||||||
|
useQueryCopilot.getState().refreshCorrelationId();
|
||||||
|
const serverInfo = useQueryCopilot.getState().notebookServerInfo;
|
||||||
|
const queryUri = userContext.features.disableCopilotPhoenixGateaway
|
||||||
|
? createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery")
|
||||||
|
: createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery");
|
||||||
|
const response = await fetch(queryUri, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
});
|
||||||
|
|
||||||
|
const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json();
|
||||||
|
if (response.ok) {
|
||||||
|
if (generateSQLQueryResponse?.sql !== "N/A") {
|
||||||
|
let query = `-- **Prompt:** ${userPrompt}\r\n`;
|
||||||
|
if (generateSQLQueryResponse.explanation) {
|
||||||
|
query += `-- **Explanation of query:** ${generateSQLQueryResponse.explanation}\r\n`;
|
||||||
|
}
|
||||||
|
query += generateSQLQueryResponse.sql;
|
||||||
|
setQuery(query);
|
||||||
|
setGeneratedQuery(generateSQLQueryResponse.sql);
|
||||||
|
setGeneratedQueryComments(generateSQLQueryResponse.explanation);
|
||||||
|
setShowFeedbackBar(true);
|
||||||
|
resetQueryResults();
|
||||||
|
} else {
|
||||||
|
setShowInvalidQueryMessageBar(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleError(JSON.stringify(generateSQLQueryResponse), "copilotInternalServerError");
|
||||||
|
useTabs.getState().setIsQueryErrorThrown(true);
|
||||||
|
setShowErrorMessageBar(true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "executeNaturalLanguageQuery");
|
||||||
|
useTabs.getState().setIsQueryErrorThrown(true);
|
||||||
|
setShowErrorMessageBar(true);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
setIsGeneratingQuery(false);
|
||||||
|
useTabs.getState().setIsTabExecuting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const showTeachingBubble = (): void => {
|
||||||
|
if (!inputEdited.current) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!inputEdited.current) {
|
||||||
|
toggleCopilotTeachingBubbleVisible();
|
||||||
|
inputEdited.current = true;
|
||||||
|
}
|
||||||
|
}, 30000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearFeedback = () => {
|
||||||
|
resetButtonState();
|
||||||
|
resetQueryResults();
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetButtonState = () => {
|
||||||
|
setDislikeQuery(false);
|
||||||
|
setLikeQuery(false);
|
||||||
|
setShowCallout(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const startGenerateQueryProcess = () => {
|
||||||
|
updateHistories();
|
||||||
|
generateSQLQuery();
|
||||||
|
resetButtonState();
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
showTeachingBubble();
|
||||||
|
useTabs.getState().setIsQueryErrorThrown(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack className="copilot-prompt-pane" styles={{ root: { backgroundColor: "#FAFAFA", padding: "16px 24px 0px" } }}>
|
||||||
|
<Stack horizontal>
|
||||||
|
<Image src={CopilotIcon} style={{ width: 24, height: 24 }} />
|
||||||
|
<Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ imageProps: { src: errorIcon } }}
|
||||||
|
onClick={() => {
|
||||||
|
toggleCopilot(false);
|
||||||
|
clearFeedback();
|
||||||
|
resetMessageStates();
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
marginLeft: "auto !important",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
ariaLabel="Close"
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
<Stack horizontal verticalAlign="center">
|
||||||
|
<TextField
|
||||||
|
id="naturalLanguageInput"
|
||||||
|
value={userPrompt}
|
||||||
|
onChange={handleUserPromptChange}
|
||||||
|
onClick={() => {
|
||||||
|
inputEdited.current = true;
|
||||||
|
setShowSamplePrompts(true);
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
inputEdited.current = true;
|
||||||
|
startGenerateQueryProcess();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{ lineHeight: 30 }}
|
||||||
|
styles={{ root: { width: "95%" }, fieldGroup: { borderRadius: 6 } }}
|
||||||
|
disabled={isGeneratingQuery}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
{copilotTeachingBubbleVisible && (
|
||||||
|
<TeachingBubble
|
||||||
|
calloutProps={{ directionalHint: DirectionalHint.bottomCenter }}
|
||||||
|
target="#naturalLanguageInput"
|
||||||
|
hasCloseButton={true}
|
||||||
|
closeButtonAriaLabel="Close"
|
||||||
|
onDismiss={toggleCopilotTeachingBubbleVisible}
|
||||||
|
hasSmallHeadline={true}
|
||||||
|
headline="Write a prompt"
|
||||||
|
>
|
||||||
|
Write a prompt here and Copilot will generate the query for you. You can also choose from our{" "}
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
setShowSamplePrompts(true);
|
||||||
|
toggleCopilotTeachingBubbleVisible();
|
||||||
|
}}
|
||||||
|
style={{ color: "white", fontWeight: 600 }}
|
||||||
|
>
|
||||||
|
sample prompts
|
||||||
|
</Link>{" "}
|
||||||
|
or write your own query
|
||||||
|
</TeachingBubble>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Send" }}
|
||||||
|
disabled={isGeneratingQuery || !userPrompt.trim()}
|
||||||
|
style={{ marginLeft: 8 }}
|
||||||
|
onClick={() => startGenerateQueryProcess()}
|
||||||
|
/>
|
||||||
|
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
||||||
|
{showSamplePrompts && (
|
||||||
|
<Callout
|
||||||
|
styles={{ root: { minWidth: 400 } }}
|
||||||
|
target="#naturalLanguageInput"
|
||||||
|
isBeakVisible={false}
|
||||||
|
onDismiss={() => setShowSamplePrompts(false)}
|
||||||
|
directionalHintFixed={true}
|
||||||
|
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||||
|
alignTargetEdge={true}
|
||||||
|
gapSpace={4}
|
||||||
|
>
|
||||||
|
<Stack>
|
||||||
|
{filteredHistories?.length > 0 && (
|
||||||
|
<Stack>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "#0078D4",
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: "4px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Recent
|
||||||
|
</Text>
|
||||||
|
{filteredHistories.map((history, i) => (
|
||||||
|
<DefaultButton
|
||||||
|
key={i}
|
||||||
|
onClick={() => {
|
||||||
|
setUserPrompt(history);
|
||||||
|
setShowSamplePrompts(false);
|
||||||
|
inputEdited.current = true;
|
||||||
|
}}
|
||||||
|
onRenderIcon={() => <Image src={RecentIcon} />}
|
||||||
|
styles={promptStyles}
|
||||||
|
>
|
||||||
|
{history}
|
||||||
|
</DefaultButton>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{filteredSuggestedPrompts?.length > 0 && (
|
||||||
|
<Stack>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "#0078D4",
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: "4px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Suggested Prompts
|
||||||
|
</Text>
|
||||||
|
{filteredSuggestedPrompts.map((prompt) => (
|
||||||
|
<DefaultButton
|
||||||
|
key={prompt.id}
|
||||||
|
onClick={() => {
|
||||||
|
setUserPrompt(prompt.text);
|
||||||
|
setShowSamplePrompts(false);
|
||||||
|
inputEdited.current = true;
|
||||||
|
}}
|
||||||
|
onRenderIcon={() => <Image src={HintIcon} />}
|
||||||
|
styles={promptStyles}
|
||||||
|
>
|
||||||
|
{prompt.text}
|
||||||
|
</DefaultButton>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{(filteredHistories?.length > 0 || filteredSuggestedPrompts?.length > 0) && (
|
||||||
|
<Stack>
|
||||||
|
<Separator
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
selectors: { "::before": { background: "#E1DFDD" } },
|
||||||
|
padding: 0,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
fontSize: 14,
|
||||||
|
marginLeft: 16,
|
||||||
|
padding: "4px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Learn about{" "}
|
||||||
|
<Link target="_blank" href="https://aka.ms/cdb-copilot-writing">
|
||||||
|
writing effective prompts
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Callout>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack style={{ margin: "8px 0" }}>
|
||||||
|
<Text style={{ fontSize: 12 }}>
|
||||||
|
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
||||||
|
<Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank">
|
||||||
|
Read preview terms
|
||||||
|
</Link>
|
||||||
|
{showErrorMessageBar && (
|
||||||
|
<MessageBar messageBarType={MessageBarType.error}>
|
||||||
|
We ran into an error and were not able to execute query.
|
||||||
|
</MessageBar>
|
||||||
|
)}
|
||||||
|
{showInvalidQueryMessageBar && (
|
||||||
|
<MessageBar
|
||||||
|
messageBarType={MessageBarType.info}
|
||||||
|
styles={{ root: { backgroundColor: "#F0F6FF" }, icon: { color: "#015CDA" } }}
|
||||||
|
>
|
||||||
|
We were unable to generate a query based upon the prompt provided. Please modify the prompt and try again.
|
||||||
|
For examples of how to write a good prompt, please read
|
||||||
|
<Link href="https://aka.ms/cdb-copilot-writing" target="_blank">
|
||||||
|
this article.
|
||||||
|
</Link>{" "}
|
||||||
|
Our content guidelines can be found
|
||||||
|
<Link href="https://aka.ms/cdb-query-copilot" target="_blank">
|
||||||
|
here.
|
||||||
|
</Link>
|
||||||
|
</MessageBar>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{showFeedbackBar && (
|
||||||
|
<Stack style={{ backgroundColor: "#FFF8F0", padding: "2px 8px" }} horizontal verticalAlign="center">
|
||||||
|
<Text style={{ fontWeight: 600, fontSize: 12 }}>Provide feedback on the query generated</Text>
|
||||||
|
{showCallout && !hideFeedbackModalForLikedQueries && (
|
||||||
|
<Callout
|
||||||
|
style={{ padding: 8 }}
|
||||||
|
target="#likeBtn"
|
||||||
|
onDismiss={() => {
|
||||||
|
setShowCallout(false);
|
||||||
|
SubmitFeedback({
|
||||||
|
params: {
|
||||||
|
generatedQuery: generatedQuery,
|
||||||
|
likeQuery: likeQuery,
|
||||||
|
description: "",
|
||||||
|
userPrompt: userPrompt,
|
||||||
|
},
|
||||||
|
explorer: explorer,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
directionalHint={DirectionalHint.topCenter}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
Thank you. Need to give{" "}
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
setShowCallout(false);
|
||||||
|
useQueryCopilot.getState().openFeedbackModal(generatedQuery, true, userPrompt);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
more feedback?
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</Callout>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
id="likeBtn"
|
||||||
|
style={{ marginLeft: 20 }}
|
||||||
|
iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }}
|
||||||
|
onClick={() => {
|
||||||
|
setShowCallout(!likeQuery);
|
||||||
|
setLikeQuery(!likeQuery);
|
||||||
|
if (dislikeQuery) {
|
||||||
|
setDislikeQuery(!dislikeQuery);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
style={{ margin: "0 10px" }}
|
||||||
|
iconProps={{ iconName: dislikeQuery === true ? "DislikeSolid" : "Dislike" }}
|
||||||
|
onClick={() => {
|
||||||
|
if (!dislikeQuery) {
|
||||||
|
useQueryCopilot.getState().openFeedbackModal(generatedQuery, false, userPrompt);
|
||||||
|
setLikeQuery(false);
|
||||||
|
}
|
||||||
|
setDislikeQuery(!dislikeQuery);
|
||||||
|
setShowCallout(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Separator vertical style={{ color: "#EDEBE9" }} />
|
||||||
|
<CommandBarButton
|
||||||
|
onClick={copyGeneratedCode}
|
||||||
|
iconProps={{ iconName: "Copy" }}
|
||||||
|
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
||||||
|
>
|
||||||
|
Copy code
|
||||||
|
</CommandBarButton>
|
||||||
|
<CommandBarButton
|
||||||
|
onClick={() => {
|
||||||
|
setShowDeletePopup(true);
|
||||||
|
}}
|
||||||
|
iconProps={{ iconName: "Delete" }}
|
||||||
|
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
||||||
|
>
|
||||||
|
Delete code
|
||||||
|
</CommandBarButton>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
<WelcomeModal visible={localStorage.getItem("hideWelcomeModal") !== "true"} />
|
||||||
|
{isSamplePromptsOpen && <SamplePrompts sampleProps={sampleProps} />}
|
||||||
|
{query !== "" && query.trim().length !== 0 && (
|
||||||
|
<DeletePopup
|
||||||
|
showDeletePopup={showDeletePopup}
|
||||||
|
setShowDeletePopup={setShowDeletePopup}
|
||||||
|
setQuery={setQuery}
|
||||||
|
clearFeedback={clearFeedback}
|
||||||
|
showFeedbackBar={setShowFeedbackBar}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<CopyPopup showCopyPopup={showCopyPopup} setShowCopyPopup={setshowCopyPopup} />
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,242 +1,28 @@
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import {
|
import { Stack } from "@fluentui/react";
|
||||||
Callout,
|
|
||||||
CommandBarButton,
|
|
||||||
DefaultButton,
|
|
||||||
DirectionalHint,
|
|
||||||
IButtonStyles,
|
|
||||||
IconButton,
|
|
||||||
Image,
|
|
||||||
Link,
|
|
||||||
MessageBar,
|
|
||||||
MessageBarType,
|
|
||||||
Separator,
|
|
||||||
Spinner,
|
|
||||||
Stack,
|
|
||||||
TeachingBubble,
|
|
||||||
Text,
|
|
||||||
TextField,
|
|
||||||
} from "@fluentui/react";
|
|
||||||
import { useBoolean } from "@fluentui/react-hooks";
|
|
||||||
import {
|
|
||||||
ContainerStatusType,
|
|
||||||
PoolIdType,
|
|
||||||
QueryCopilotSampleContainerSchema,
|
|
||||||
ShortenedQueryCopilotSampleContainerSchema,
|
|
||||||
} from "Common/Constants";
|
|
||||||
import { handleError } from "Common/ErrorHandlingUtils";
|
|
||||||
import { createUri } from "Common/UrlUtility";
|
|
||||||
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
|
||||||
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import { SaveQueryPane } from "Explorer/Panes/SaveQueryPane/SaveQueryPane";
|
import { SaveQueryPane } from "Explorer/Panes/SaveQueryPane/SaveQueryPane";
|
||||||
import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
|
import { QueryCopilotPromptbar } from "Explorer/QueryCopilot/QueryCopilotPromptbar";
|
||||||
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
|
import { OnExecuteQueryClick } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
||||||
import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
|
import { QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
||||||
import { OnExecuteQueryClick, SubmitFeedback } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
|
||||||
import { GenerateSQLQueryResponse, QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
|
||||||
import { QueryCopilotResults } from "Explorer/QueryCopilot/Shared/QueryCopilotResults";
|
import { QueryCopilotResults } from "Explorer/QueryCopilot/Shared/QueryCopilotResults";
|
||||||
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/Shared/SamplePrompts/SamplePrompts";
|
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import React, { useRef, useState } from "react";
|
import React, { useState } from "react";
|
||||||
import SplitterLayout from "react-splitter-layout";
|
import SplitterLayout from "react-splitter-layout";
|
||||||
|
import QueryCommandIcon from "../../../images/CopilotCommand.svg";
|
||||||
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
||||||
import HintIcon from "../../../images/Hint.svg";
|
|
||||||
import CopilotIcon from "../../../images/QueryCopilotNewLogo.svg";
|
|
||||||
import RecentIcon from "../../../images/Recent.svg";
|
|
||||||
import SaveQueryIcon from "../../../images/save-cosmos.svg";
|
import SaveQueryIcon from "../../../images/save-cosmos.svg";
|
||||||
import { useTabs } from "../../hooks/useTabs";
|
import * as StringUtility from "../../Shared/StringUtility";
|
||||||
|
|
||||||
interface SuggestedPrompt {
|
|
||||||
id: number;
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const promptStyles: IButtonStyles = {
|
|
||||||
root: { border: 0, selectors: { ":hover": { outline: "1px dashed #605e5c" } } },
|
|
||||||
label: { fontWeight: 400, textAlign: "left", paddingLeft: 8 },
|
|
||||||
};
|
|
||||||
|
|
||||||
export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: QueryCopilotProps): JSX.Element => {
|
export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: QueryCopilotProps): JSX.Element => {
|
||||||
const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
|
const { query, setQuery, selectedQuery, setSelectedQuery, isGeneratingQuery } = useQueryCopilot();
|
||||||
const inputEdited = useRef(false);
|
|
||||||
const {
|
|
||||||
hideFeedbackModalForLikedQueries,
|
|
||||||
userPrompt,
|
|
||||||
setUserPrompt,
|
|
||||||
generatedQuery,
|
|
||||||
setGeneratedQuery,
|
|
||||||
query,
|
|
||||||
setQuery,
|
|
||||||
selectedQuery,
|
|
||||||
setSelectedQuery,
|
|
||||||
isGeneratingQuery,
|
|
||||||
setIsGeneratingQuery,
|
|
||||||
likeQuery,
|
|
||||||
setLikeQuery,
|
|
||||||
dislikeQuery,
|
|
||||||
setDislikeQuery,
|
|
||||||
showCallout,
|
|
||||||
setShowCallout,
|
|
||||||
showSamplePrompts,
|
|
||||||
setShowSamplePrompts,
|
|
||||||
isSamplePromptsOpen,
|
|
||||||
setIsSamplePromptsOpen,
|
|
||||||
showDeletePopup,
|
|
||||||
setShowDeletePopup,
|
|
||||||
showFeedbackBar,
|
|
||||||
setShowFeedbackBar,
|
|
||||||
showCopyPopup,
|
|
||||||
setshowCopyPopup,
|
|
||||||
showErrorMessageBar,
|
|
||||||
showInvalidQueryMessageBar,
|
|
||||||
setShowInvalidQueryMessageBar,
|
|
||||||
setShowErrorMessageBar,
|
|
||||||
setGeneratedQueryComments,
|
|
||||||
setQueryResults,
|
|
||||||
setErrorMessage,
|
|
||||||
} = useQueryCopilot();
|
|
||||||
|
|
||||||
const sampleProps: SamplePromptsProps = {
|
const cachedCopilotToggleStatus = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotToggleStatus`);
|
||||||
isSamplePromptsOpen: isSamplePromptsOpen,
|
const [copilotActive, setCopilotActive] = useState<boolean>(StringUtility.toBoolean(cachedCopilotToggleStatus));
|
||||||
setIsSamplePromptsOpen: setIsSamplePromptsOpen,
|
|
||||||
setTextBox: setUserPrompt,
|
|
||||||
};
|
|
||||||
|
|
||||||
const copyGeneratedCode = () => {
|
|
||||||
if (!query) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const queryElement = document.createElement("textarea");
|
|
||||||
queryElement.value = query;
|
|
||||||
document.body.appendChild(queryElement);
|
|
||||||
queryElement.select();
|
|
||||||
document.execCommand("copy");
|
|
||||||
document.body.removeChild(queryElement);
|
|
||||||
|
|
||||||
setshowCopyPopup(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setshowCopyPopup(false);
|
|
||||||
}, 6000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const cachedHistoriesString = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotHistories`);
|
|
||||||
const cachedHistories = cachedHistoriesString?.split("|");
|
|
||||||
const [histories, setHistories] = useState<string[]>(cachedHistories || []);
|
|
||||||
const suggestedPrompts: SuggestedPrompt[] = [
|
|
||||||
{ id: 1, text: 'Show all products that have the word "ultra" in the name or description' },
|
|
||||||
{ id: 2, text: "What are all of the possible categories for the products, and their counts?" },
|
|
||||||
{ id: 3, text: 'Show me all products that have been reviewed by someone with a username that contains "bob"' },
|
|
||||||
];
|
|
||||||
const [filteredHistories, setFilteredHistories] = useState<string[]>(histories);
|
|
||||||
const [filteredSuggestedPrompts, setFilteredSuggestedPrompts] = useState<SuggestedPrompt[]>(suggestedPrompts);
|
|
||||||
|
|
||||||
const handleUserPromptChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
inputEdited.current = true;
|
|
||||||
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 formattedUserPrompt = userPrompt.replace(/\s+/g, " ").trim();
|
|
||||||
const existingHistories = histories.map((history) => history.replace(/\s+/g, " ").trim());
|
|
||||||
|
|
||||||
const updatedHistories = existingHistories.filter(
|
|
||||||
(history) => history.toLowerCase() !== formattedUserPrompt.toLowerCase(),
|
|
||||||
);
|
|
||||||
const newHistories = [formattedUserPrompt, ...updatedHistories.slice(0, 2)];
|
|
||||||
|
|
||||||
setHistories(newHistories);
|
|
||||||
localStorage.setItem(`${userContext.databaseAccount.id}-queryCopilotHistories`, newHistories.join("|"));
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetMessageStates = (): void => {
|
|
||||||
setShowErrorMessageBar(false);
|
|
||||||
setShowInvalidQueryMessageBar(false);
|
|
||||||
setShowFeedbackBar(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetQueryResults = (): void => {
|
|
||||||
setQueryResults(null);
|
|
||||||
setErrorMessage("");
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateSQLQuery = async (): Promise<void> => {
|
|
||||||
try {
|
|
||||||
resetMessageStates();
|
|
||||||
setIsGeneratingQuery(true);
|
|
||||||
setShowDeletePopup(false);
|
|
||||||
useTabs.getState().setIsTabExecuting(true);
|
|
||||||
useTabs.getState().setIsQueryErrorThrown(false);
|
|
||||||
if (
|
|
||||||
useQueryCopilot.getState().containerStatus.status !== ContainerStatusType.Active &&
|
|
||||||
!userContext.features.disableCopilotPhoenixGateaway
|
|
||||||
) {
|
|
||||||
await explorer.allocateContainer(PoolIdType.QueryCopilot);
|
|
||||||
}
|
|
||||||
const payload = {
|
|
||||||
containerSchema: userContext.features.enableCopilotFullSchema
|
|
||||||
? QueryCopilotSampleContainerSchema
|
|
||||||
: ShortenedQueryCopilotSampleContainerSchema,
|
|
||||||
userPrompt: userPrompt,
|
|
||||||
};
|
|
||||||
useQueryCopilot.getState().refreshCorrelationId();
|
|
||||||
const serverInfo = useQueryCopilot.getState().notebookServerInfo;
|
|
||||||
const queryUri = userContext.features.disableCopilotPhoenixGateaway
|
|
||||||
? createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery")
|
|
||||||
: createUri(serverInfo.notebookServerEndpoint, "generateSQLQuery");
|
|
||||||
const response = await fetch(queryUri, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"content-type": "application/json",
|
|
||||||
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(payload),
|
|
||||||
});
|
|
||||||
|
|
||||||
const generateSQLQueryResponse: GenerateSQLQueryResponse = await response?.json();
|
|
||||||
if (response.ok) {
|
|
||||||
if (generateSQLQueryResponse?.sql !== "N/A") {
|
|
||||||
let query = `-- **Prompt:** ${userPrompt}\r\n`;
|
|
||||||
if (generateSQLQueryResponse.explanation) {
|
|
||||||
query += `-- **Explanation of query:** ${generateSQLQueryResponse.explanation}\r\n`;
|
|
||||||
}
|
|
||||||
query += generateSQLQueryResponse.sql;
|
|
||||||
setQuery(query);
|
|
||||||
setGeneratedQuery(generateSQLQueryResponse.sql);
|
|
||||||
setGeneratedQueryComments(generateSQLQueryResponse.explanation);
|
|
||||||
setShowFeedbackBar(true);
|
|
||||||
resetQueryResults();
|
|
||||||
} else {
|
|
||||||
setShowInvalidQueryMessageBar(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handleError(JSON.stringify(generateSQLQueryResponse), "copilotInternalServerError");
|
|
||||||
useTabs.getState().setIsQueryErrorThrown(true);
|
|
||||||
setShowErrorMessageBar(true);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
handleError(error, "executeNaturalLanguageQuery");
|
|
||||||
useTabs.getState().setIsQueryErrorThrown(true);
|
|
||||||
setShowErrorMessageBar(true);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
setIsGeneratingQuery(false);
|
|
||||||
useTabs.getState().setIsTabExecuting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCommandbarButtons = (): CommandButtonComponentProps[] => {
|
const getCommandbarButtons = (): CommandButtonComponentProps[] => {
|
||||||
const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query";
|
const executeQueryBtnLabel = selectedQuery ? "Execute Selection" : "Execute Query";
|
||||||
|
@ -261,322 +47,45 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
||||||
disabled: true,
|
disabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sample Prompts temporary disabled due current design
|
const toggleCopilotButton = {
|
||||||
// const samplePromptsBtn = {
|
iconSrc: QueryCommandIcon,
|
||||||
// iconSrc: SamplePromptsIcon,
|
iconAlt: "Copilot",
|
||||||
// iconAlt: "Sample Prompts",
|
onCommandClick: () => {
|
||||||
// onCommandClick: () => setIsSamplePromptsOpen(true),
|
toggleCopilot(true);
|
||||||
// commandButtonLabel: "Sample Prompts",
|
},
|
||||||
// ariaLabel: "Sample Prompts",
|
commandButtonLabel: "Copilot",
|
||||||
// hasPopup: false,
|
ariaLabel: "Copilot",
|
||||||
// };
|
hasPopup: false,
|
||||||
|
disabled: copilotActive,
|
||||||
|
};
|
||||||
|
|
||||||
return [executeQueryBtn, saveQueryBtn];
|
return [executeQueryBtn, saveQueryBtn, toggleCopilotButton];
|
||||||
};
|
|
||||||
const showTeachingBubble = (): void => {
|
|
||||||
if (!inputEdited.current) {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!inputEdited.current) {
|
|
||||||
toggleCopilotTeachingBubbleVisible();
|
|
||||||
inputEdited.current = true;
|
|
||||||
}
|
|
||||||
}, 30000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearFeedback = () => {
|
|
||||||
resetButtonState();
|
|
||||||
resetQueryResults();
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetButtonState = () => {
|
|
||||||
setDislikeQuery(false);
|
|
||||||
setLikeQuery(false);
|
|
||||||
setShowCallout(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const startGenerateQueryProcess = () => {
|
|
||||||
updateHistories();
|
|
||||||
generateSQLQuery();
|
|
||||||
resetButtonState();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
useCommandBar.getState().setContextButtons(getCommandbarButtons());
|
useCommandBar.getState().setContextButtons(getCommandbarButtons());
|
||||||
}, [query, selectedQuery]);
|
}, [query, selectedQuery, copilotActive]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
showTeachingBubble();
|
return () => {
|
||||||
useTabs.getState().setIsQueryErrorThrown(false);
|
const commandbarButtons = getCommandbarButtons();
|
||||||
|
commandbarButtons.pop();
|
||||||
|
commandbarButtons.map((props: CommandButtonComponentProps) => (props.disabled = true));
|
||||||
|
useCommandBar.getState().setContextButtons(commandbarButtons);
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const toggleCopilot = (toggle: boolean) => {
|
||||||
|
setCopilotActive(toggle);
|
||||||
|
localStorage.setItem(`${userContext.databaseAccount?.id}-queryCopilotToggleStatus`, toggle.toString());
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className="tab-pane" style={{ padding: 24, width: "100%" }}>
|
<Stack className="tab-pane" style={{ width: "100%" }}>
|
||||||
<div style={isGeneratingQuery ? { height: "100%" } : { overflowY: "auto", height: "100%" }}>
|
<div style={isGeneratingQuery ? { height: "100%" } : { overflowY: "auto", height: "100%" }}>
|
||||||
<Stack horizontal verticalAlign="center">
|
{copilotActive && (
|
||||||
<Image src={CopilotIcon} style={{ width: 24, height: 24 }} />
|
<QueryCopilotPromptbar explorer={explorer} toggleCopilot={toggleCopilot}></QueryCopilotPromptbar>
|
||||||
<Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text>
|
|
||||||
</Stack>
|
|
||||||
<Stack horizontal verticalAlign="center" style={{ marginTop: 16, width: "100%", position: "relative" }}>
|
|
||||||
<TextField
|
|
||||||
id="naturalLanguageInput"
|
|
||||||
value={userPrompt}
|
|
||||||
onChange={handleUserPromptChange}
|
|
||||||
onClick={() => {
|
|
||||||
inputEdited.current = true;
|
|
||||||
setShowSamplePrompts(true);
|
|
||||||
}}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
inputEdited.current = true;
|
|
||||||
startGenerateQueryProcess();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{ lineHeight: 30 }}
|
|
||||||
styles={{ root: { width: "95%" } }}
|
|
||||||
disabled={isGeneratingQuery}
|
|
||||||
autoComplete="off"
|
|
||||||
/>
|
|
||||||
{copilotTeachingBubbleVisible && (
|
|
||||||
<TeachingBubble
|
|
||||||
calloutProps={{ directionalHint: DirectionalHint.bottomCenter }}
|
|
||||||
target="#naturalLanguageInput"
|
|
||||||
hasCloseButton={true}
|
|
||||||
closeButtonAriaLabel="Close"
|
|
||||||
onDismiss={toggleCopilotTeachingBubbleVisible}
|
|
||||||
hasSmallHeadline={true}
|
|
||||||
headline="Write a prompt"
|
|
||||||
>
|
|
||||||
Write a prompt here and Copilot will generate the query for you. You can also choose from our{" "}
|
|
||||||
<Link
|
|
||||||
onClick={() => {
|
|
||||||
setShowSamplePrompts(true);
|
|
||||||
toggleCopilotTeachingBubbleVisible();
|
|
||||||
}}
|
|
||||||
style={{ color: "white", fontWeight: 600 }}
|
|
||||||
>
|
|
||||||
sample prompts
|
|
||||||
</Link>{" "}
|
|
||||||
or write your own query
|
|
||||||
</TeachingBubble>
|
|
||||||
)}
|
|
||||||
<IconButton
|
|
||||||
iconProps={{ iconName: "Send" }}
|
|
||||||
disabled={isGeneratingQuery || !userPrompt.trim()}
|
|
||||||
style={{ marginLeft: 8 }}
|
|
||||||
onClick={() => startGenerateQueryProcess()}
|
|
||||||
/>
|
|
||||||
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
|
||||||
{showSamplePrompts && (
|
|
||||||
<Callout
|
|
||||||
styles={{ root: { minWidth: 400 } }}
|
|
||||||
target="#naturalLanguageInput"
|
|
||||||
isBeakVisible={false}
|
|
||||||
onDismiss={() => setShowSamplePrompts(false)}
|
|
||||||
directionalHintFixed={true}
|
|
||||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
|
||||||
alignTargetEdge={true}
|
|
||||||
gapSpace={4}
|
|
||||||
>
|
|
||||||
<Stack>
|
|
||||||
{filteredHistories?.length > 0 && (
|
|
||||||
<Stack>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 600,
|
|
||||||
color: "#0078D4",
|
|
||||||
marginLeft: 16,
|
|
||||||
padding: "4px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Recent
|
|
||||||
</Text>
|
|
||||||
{filteredHistories.map((history, i) => (
|
|
||||||
<DefaultButton
|
|
||||||
key={i}
|
|
||||||
onClick={() => {
|
|
||||||
setUserPrompt(history);
|
|
||||||
setShowSamplePrompts(false);
|
|
||||||
inputEdited.current = true;
|
|
||||||
}}
|
|
||||||
onRenderIcon={() => <Image src={RecentIcon} />}
|
|
||||||
styles={promptStyles}
|
|
||||||
>
|
|
||||||
{history}
|
|
||||||
</DefaultButton>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
{filteredSuggestedPrompts?.length > 0 && (
|
|
||||||
<Stack>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 600,
|
|
||||||
color: "#0078D4",
|
|
||||||
marginLeft: 16,
|
|
||||||
padding: "4px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Suggested Prompts
|
|
||||||
</Text>
|
|
||||||
{filteredSuggestedPrompts.map((prompt) => (
|
|
||||||
<DefaultButton
|
|
||||||
key={prompt.id}
|
|
||||||
onClick={() => {
|
|
||||||
setUserPrompt(prompt.text);
|
|
||||||
setShowSamplePrompts(false);
|
|
||||||
inputEdited.current = true;
|
|
||||||
}}
|
|
||||||
onRenderIcon={() => <Image src={HintIcon} />}
|
|
||||||
styles={promptStyles}
|
|
||||||
>
|
|
||||||
{prompt.text}
|
|
||||||
</DefaultButton>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
{(filteredHistories?.length > 0 || filteredSuggestedPrompts?.length > 0) && (
|
|
||||||
<Stack>
|
|
||||||
<Separator
|
|
||||||
styles={{
|
|
||||||
root: {
|
|
||||||
selectors: { "::before": { background: "#E1DFDD" } },
|
|
||||||
padding: 0,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
fontSize: 14,
|
|
||||||
marginLeft: 16,
|
|
||||||
padding: "4px 0",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Learn about{" "}
|
|
||||||
<Link target="_blank" href="https://aka.ms/cdb-copilot-writing">
|
|
||||||
writing effective prompts
|
|
||||||
</Link>
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
</Callout>
|
|
||||||
)}
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<Stack style={{ marginTop: 8, marginBottom: 24 }}>
|
|
||||||
<Text style={{ fontSize: 12 }}>
|
|
||||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
|
||||||
<Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank">
|
|
||||||
Read preview terms
|
|
||||||
</Link>
|
|
||||||
{showErrorMessageBar && (
|
|
||||||
<MessageBar messageBarType={MessageBarType.error}>
|
|
||||||
We ran into an error and were not able to execute query.
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
{showInvalidQueryMessageBar && (
|
|
||||||
<MessageBar
|
|
||||||
messageBarType={MessageBarType.info}
|
|
||||||
styles={{ root: { backgroundColor: "#F0F6FF" }, icon: { color: "#015CDA" } }}
|
|
||||||
>
|
|
||||||
We were unable to generate a query based upon the prompt provided. Please modify the prompt and try
|
|
||||||
again. For examples of how to write a good prompt, please read
|
|
||||||
<Link href="https://aka.ms/cdb-copilot-writing" target="_blank">
|
|
||||||
this article.
|
|
||||||
</Link>{" "}
|
|
||||||
Our content guidelines can be found
|
|
||||||
<Link href="https://aka.ms/cdb-query-copilot" target="_blank">
|
|
||||||
here.
|
|
||||||
</Link>
|
|
||||||
</MessageBar>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
{showFeedbackBar && (
|
|
||||||
<Stack style={{ backgroundColor: "#FFF8F0", padding: "2px 8px" }} horizontal verticalAlign="center">
|
|
||||||
<Text style={{ fontWeight: 600, fontSize: 12 }}>Provide feedback on the query generated</Text>
|
|
||||||
{showCallout && !hideFeedbackModalForLikedQueries && (
|
|
||||||
<Callout
|
|
||||||
style={{ padding: 8 }}
|
|
||||||
target="#likeBtn"
|
|
||||||
onDismiss={() => {
|
|
||||||
setShowCallout(false);
|
|
||||||
SubmitFeedback({
|
|
||||||
params: {
|
|
||||||
generatedQuery: generatedQuery,
|
|
||||||
likeQuery: likeQuery,
|
|
||||||
description: "",
|
|
||||||
userPrompt: userPrompt,
|
|
||||||
},
|
|
||||||
explorer: explorer,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
directionalHint={DirectionalHint.topCenter}
|
|
||||||
>
|
|
||||||
<Text>
|
|
||||||
Thank you. Need to give{" "}
|
|
||||||
<Link
|
|
||||||
onClick={() => {
|
|
||||||
setShowCallout(false);
|
|
||||||
useQueryCopilot.getState().openFeedbackModal(generatedQuery, true, userPrompt);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
more feedback?
|
|
||||||
</Link>
|
|
||||||
</Text>
|
|
||||||
</Callout>
|
|
||||||
)}
|
|
||||||
<IconButton
|
|
||||||
id="likeBtn"
|
|
||||||
style={{ marginLeft: 20 }}
|
|
||||||
iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }}
|
|
||||||
onClick={() => {
|
|
||||||
setShowCallout(!likeQuery);
|
|
||||||
setLikeQuery(!likeQuery);
|
|
||||||
if (dislikeQuery) {
|
|
||||||
setDislikeQuery(!dislikeQuery);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
style={{ margin: "0 10px" }}
|
|
||||||
iconProps={{ iconName: dislikeQuery === true ? "DislikeSolid" : "Dislike" }}
|
|
||||||
onClick={() => {
|
|
||||||
if (!dislikeQuery) {
|
|
||||||
useQueryCopilot.getState().openFeedbackModal(generatedQuery, false, userPrompt);
|
|
||||||
setLikeQuery(false);
|
|
||||||
}
|
|
||||||
setDislikeQuery(!dislikeQuery);
|
|
||||||
setShowCallout(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Separator vertical style={{ color: "#EDEBE9" }} />
|
|
||||||
<CommandBarButton
|
|
||||||
onClick={copyGeneratedCode}
|
|
||||||
iconProps={{ iconName: "Copy" }}
|
|
||||||
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
|
||||||
>
|
|
||||||
Copy code
|
|
||||||
</CommandBarButton>
|
|
||||||
<CommandBarButton
|
|
||||||
onClick={() => {
|
|
||||||
setShowDeletePopup(true);
|
|
||||||
}}
|
|
||||||
iconProps={{ iconName: "Delete" }}
|
|
||||||
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
|
||||||
>
|
|
||||||
Delete code
|
|
||||||
</CommandBarButton>
|
|
||||||
</Stack>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Stack className="tabPaneContentContainer">
|
<Stack className="tabPaneContentContainer">
|
||||||
<SplitterLayout vertical={true} primaryIndex={0} primaryMinSize={100} secondaryMinSize={200}>
|
<SplitterLayout vertical={true} primaryIndex={0} primaryMinSize={100} secondaryMinSize={200}>
|
||||||
<EditorReact
|
<EditorReact
|
||||||
|
@ -592,18 +101,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotProps> = ({ explorer }: Query
|
||||||
<QueryCopilotResults />
|
<QueryCopilotResults />
|
||||||
</SplitterLayout>
|
</SplitterLayout>
|
||||||
</Stack>
|
</Stack>
|
||||||
<WelcomeModal visible={localStorage.getItem("hideWelcomeModal") !== "true"} />
|
|
||||||
{isSamplePromptsOpen && <SamplePrompts sampleProps={sampleProps} />}
|
|
||||||
{query !== "" && query.trim().length !== 0 && (
|
|
||||||
<DeletePopup
|
|
||||||
showDeletePopup={showDeletePopup}
|
|
||||||
setShowDeletePopup={setShowDeletePopup}
|
|
||||||
setQuery={setQuery}
|
|
||||||
clearFeedback={clearFeedback}
|
|
||||||
showFeedbackBar={setShowFeedbackBar}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<CopyPopup showCopyPopup={showCopyPopup} setShowCopyPopup={setshowCopyPopup} />
|
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { userContext } from "UserContext";
|
||||||
import { queryPagesUntilContentPresent } from "Utils/QueryUtils";
|
import { queryPagesUntilContentPresent } from "Utils/QueryUtils";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import { useTabs } from "hooks/useTabs";
|
import { useTabs } from "hooks/useTabs";
|
||||||
|
import * as StringUtility from "../../../Shared/StringUtility";
|
||||||
|
|
||||||
export const SendQueryRequest = async ({
|
export const SendQueryRequest = async ({
|
||||||
userPrompt,
|
userPrompt,
|
||||||
|
@ -184,15 +185,20 @@ export const QueryDocumentsPerPage = async (
|
||||||
correlationId: useQueryCopilot.getState().correlationId,
|
correlationId: useQueryCopilot.getState().correlationId,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const isCopilotActive = StringUtility.toBoolean(
|
||||||
|
localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotToggleStatus`),
|
||||||
|
);
|
||||||
const errorMessage = getErrorMessage(error);
|
const errorMessage = getErrorMessage(error);
|
||||||
traceFailure(Action.ExecuteQueryGeneratedFromQueryCopilot, {
|
traceFailure(Action.ExecuteQueryGeneratedFromQueryCopilot, {
|
||||||
correlationId: useQueryCopilot.getState().correlationId,
|
correlationId: useQueryCopilot.getState().correlationId,
|
||||||
errorMessage: errorMessage,
|
errorMessage: errorMessage,
|
||||||
});
|
});
|
||||||
useQueryCopilot.getState().setErrorMessage(errorMessage);
|
|
||||||
handleError(errorMessage, "executeQueryCopilotTab");
|
handleError(errorMessage, "executeQueryCopilotTab");
|
||||||
useTabs.getState().setIsQueryErrorThrown(true);
|
useTabs.getState().setIsQueryErrorThrown(true);
|
||||||
useQueryCopilot.getState().setShowErrorMessageBar(true);
|
if (isCopilotActive) {
|
||||||
|
useQueryCopilot.getState().setErrorMessage(errorMessage);
|
||||||
|
useQueryCopilot.getState().setShowErrorMessageBar(true);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
useQueryCopilot.getState().setIsExecuting(false);
|
useQueryCopilot.getState().setIsExecuting(false);
|
||||||
useTabs.getState().setIsTabExecuting(false);
|
useTabs.getState().setIsTabExecuting(false);
|
||||||
|
|
|
@ -5,7 +5,6 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
||||||
className="tab-pane"
|
className="tab-pane"
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"padding": 24,
|
|
||||||
"width": "100%",
|
"width": "100%",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,103 +17,6 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
verticalAlign="center"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src={Object {}}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"height": 24,
|
|
||||||
"width": 24,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": 600,
|
|
||||||
"marginLeft": 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Copilot
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
<Stack
|
|
||||||
horizontal={true}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"marginTop": 16,
|
|
||||||
"position": "relative",
|
|
||||||
"width": "100%",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verticalAlign="center"
|
|
||||||
>
|
|
||||||
<StyledTextFieldBase
|
|
||||||
autoComplete="off"
|
|
||||||
disabled={false}
|
|
||||||
id="naturalLanguageInput"
|
|
||||||
onChange={[Function]}
|
|
||||||
onClick={[Function]}
|
|
||||||
onKeyDown={[Function]}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"lineHeight": 30,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
styles={
|
|
||||||
Object {
|
|
||||||
"root": Object {
|
|
||||||
"width": "95%",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
<CustomizedIconButton
|
|
||||||
disabled={true}
|
|
||||||
iconProps={
|
|
||||||
Object {
|
|
||||||
"iconName": "Send",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClick={[Function]}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"marginLeft": 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
<Stack
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"marginBottom": 24,
|
|
||||||
"marginTop": 8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"fontSize": 12,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.
|
|
||||||
|
|
||||||
<StyledLinkBase
|
|
||||||
href="https://aka.ms/cdb-copilot-preview-terms"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Read preview terms
|
|
||||||
</StyledLinkBase>
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
<Stack
|
<Stack
|
||||||
className="tabPaneContentContainer"
|
className="tabPaneContentContainer"
|
||||||
>
|
>
|
||||||
|
@ -142,20 +44,6 @@ exports[`Query copilot tab snapshot test should render with initial input 1`] =
|
||||||
<QueryCopilotResults />
|
<QueryCopilotResults />
|
||||||
</t>
|
</t>
|
||||||
</Stack>
|
</Stack>
|
||||||
<WelcomeModal
|
|
||||||
visible={true}
|
|
||||||
/>
|
|
||||||
<DeletePopup
|
|
||||||
clearFeedback={[Function]}
|
|
||||||
setQuery={[Function]}
|
|
||||||
setShowDeletePopup={[Function]}
|
|
||||||
showDeletePopup={false}
|
|
||||||
showFeedbackBar={[Function]}
|
|
||||||
/>
|
|
||||||
<CopyPopup
|
|
||||||
setShowCopyPopup={[Function]}
|
|
||||||
showCopyPopup={false}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
Loading…
Reference in New Issue