2023-11-09 11:55:25 -06:00
|
|
|
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
|
|
|
/* eslint-disable no-console */
|
2023-10-09 12:42:25 -05:00
|
|
|
|
import {
|
|
|
|
|
Callout,
|
|
|
|
|
CommandBarButton,
|
|
|
|
|
DefaultButton,
|
|
|
|
|
DirectionalHint,
|
|
|
|
|
IButtonStyles,
|
|
|
|
|
IconButton,
|
|
|
|
|
Image,
|
|
|
|
|
Link,
|
|
|
|
|
MessageBar,
|
|
|
|
|
MessageBarType,
|
|
|
|
|
Separator,
|
|
|
|
|
Spinner,
|
|
|
|
|
Stack,
|
|
|
|
|
TeachingBubble,
|
|
|
|
|
Text,
|
|
|
|
|
TextField,
|
|
|
|
|
} from "@fluentui/react";
|
2023-11-10 16:55:39 -06:00
|
|
|
|
import { HttpStatusCodes } from "Common/Constants";
|
2023-10-09 12:42:25 -05:00
|
|
|
|
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";
|
2023-11-09 11:55:25 -06:00
|
|
|
|
import {
|
|
|
|
|
SuggestedPrompt,
|
|
|
|
|
getSampleDatabaseSuggestedPrompts,
|
|
|
|
|
getSuggestedPrompts,
|
|
|
|
|
} from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
|
|
|
|
import { SubmitFeedback, allocatePhoenixContainer } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
2023-10-09 12:42:25 -05:00
|
|
|
|
import { GenerateSQLQueryResponse, QueryCopilotProps } from "Explorer/QueryCopilot/Shared/QueryCopilotInterfaces";
|
|
|
|
|
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/Shared/SamplePrompts/SamplePrompts";
|
2023-11-09 11:55:25 -06:00
|
|
|
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
2023-10-09 12:42:25 -05:00
|
|
|
|
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";
|
2023-11-09 11:55:25 -06:00
|
|
|
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
2023-10-09 12:42:25 -05:00
|
|
|
|
import { useTabs } from "../../hooks/useTabs";
|
2023-11-09 11:55:25 -06:00
|
|
|
|
import { useCopilotStore } from "../QueryCopilot/QueryCopilotContext";
|
|
|
|
|
import { useSelectedNode } from "../useSelectedNode";
|
2023-10-09 12:42:25 -05:00
|
|
|
|
|
|
|
|
|
type QueryCopilotPromptProps = QueryCopilotProps & {
|
2023-11-09 11:55:25 -06:00
|
|
|
|
databaseId: string;
|
|
|
|
|
containerId: string;
|
2023-10-09 12:42:25 -05:00
|
|
|
|
toggleCopilot: (toggle: boolean) => void;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const promptStyles: IButtonStyles = {
|
|
|
|
|
root: { border: 0, selectors: { ":hover": { outline: "1px dashed #605e5c" } } },
|
2023-11-10 16:55:39 -06:00
|
|
|
|
label: {
|
|
|
|
|
fontWeight: 400,
|
|
|
|
|
textAlign: "left",
|
|
|
|
|
paddingLeft: 8,
|
|
|
|
|
overflow: "hidden",
|
|
|
|
|
whiteSpace: "nowrap",
|
|
|
|
|
textOverflow: "ellipsis",
|
|
|
|
|
},
|
|
|
|
|
textContainer: { overflow: "hidden" },
|
2023-10-09 12:42:25 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|
|
|
|
explorer,
|
|
|
|
|
toggleCopilot,
|
2023-11-09 11:55:25 -06:00
|
|
|
|
databaseId,
|
|
|
|
|
containerId,
|
2023-10-09 12:42:25 -05:00
|
|
|
|
}: QueryCopilotPromptProps): JSX.Element => {
|
2024-01-19 09:37:17 -06:00
|
|
|
|
const [copilotTeachingBubbleVisible, setCopilotTeachingBubbleVisible] = useState<boolean>(false);
|
2023-10-09 12:42:25 -05:00
|
|
|
|
const inputEdited = useRef(false);
|
|
|
|
|
const {
|
2023-11-09 11:55:25 -06:00
|
|
|
|
openFeedbackModal,
|
2023-10-09 12:42:25 -05:00
|
|
|
|
hideFeedbackModalForLikedQueries,
|
|
|
|
|
userPrompt,
|
|
|
|
|
setUserPrompt,
|
|
|
|
|
generatedQuery,
|
|
|
|
|
setGeneratedQuery,
|
|
|
|
|
query,
|
|
|
|
|
setQuery,
|
|
|
|
|
isGeneratingQuery,
|
|
|
|
|
setIsGeneratingQuery,
|
|
|
|
|
likeQuery,
|
|
|
|
|
setLikeQuery,
|
|
|
|
|
dislikeQuery,
|
|
|
|
|
setDislikeQuery,
|
|
|
|
|
showCallout,
|
|
|
|
|
setShowCallout,
|
|
|
|
|
isSamplePromptsOpen,
|
|
|
|
|
setIsSamplePromptsOpen,
|
|
|
|
|
showSamplePrompts,
|
|
|
|
|
setShowSamplePrompts,
|
2024-01-19 09:37:17 -06:00
|
|
|
|
showPromptTeachingBubble,
|
|
|
|
|
setShowPromptTeachingBubble,
|
2023-10-09 12:42:25 -05:00
|
|
|
|
showDeletePopup,
|
|
|
|
|
setShowDeletePopup,
|
|
|
|
|
showFeedbackBar,
|
|
|
|
|
setShowFeedbackBar,
|
|
|
|
|
showCopyPopup,
|
|
|
|
|
setshowCopyPopup,
|
|
|
|
|
showErrorMessageBar,
|
|
|
|
|
showInvalidQueryMessageBar,
|
|
|
|
|
setShowInvalidQueryMessageBar,
|
|
|
|
|
setShowErrorMessageBar,
|
|
|
|
|
setGeneratedQueryComments,
|
|
|
|
|
setQueryResults,
|
|
|
|
|
setErrorMessage,
|
2023-11-10 16:55:39 -06:00
|
|
|
|
errorMessage,
|
2023-11-09 11:55:25 -06:00
|
|
|
|
} = useCopilotStore();
|
2023-10-09 12:42:25 -05:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
};
|
|
|
|
|
|
2023-11-09 11:55:25 -06:00
|
|
|
|
const isSampleCopilotActive = useSelectedNode.getState().isQueryCopilotCollectionSelected();
|
2023-10-09 12:42:25 -05:00
|
|
|
|
const cachedHistoriesString = localStorage.getItem(`${userContext.databaseAccount?.id}-queryCopilotHistories`);
|
|
|
|
|
const cachedHistories = cachedHistoriesString?.split("|");
|
|
|
|
|
const [histories, setHistories] = useState<string[]>(cachedHistories || []);
|
2023-11-09 11:55:25 -06:00
|
|
|
|
const suggestedPrompts: SuggestedPrompt[] = isSampleCopilotActive
|
|
|
|
|
? getSampleDatabaseSuggestedPrompts()
|
|
|
|
|
: getSuggestedPrompts();
|
2023-10-09 12:42:25 -05:00
|
|
|
|
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);
|
2023-11-09 11:55:25 -06:00
|
|
|
|
const mode: string = isSampleCopilotActive ? "Sample" : "User";
|
|
|
|
|
|
|
|
|
|
await allocatePhoenixContainer({ explorer, databaseId, containerId, mode });
|
|
|
|
|
|
2023-10-09 12:42:25 -05:00
|
|
|
|
const payload = {
|
|
|
|
|
userPrompt: userPrompt,
|
|
|
|
|
};
|
|
|
|
|
useQueryCopilot.getState().refreshCorrelationId();
|
|
|
|
|
const serverInfo = useQueryCopilot.getState().notebookServerInfo;
|
|
|
|
|
const queryUri = userContext.features.disableCopilotPhoenixGateaway
|
|
|
|
|
? createUri("https://copilotorchestrater.azurewebsites.net/", "generateSQLQuery")
|
2023-11-10 18:53:23 -06:00
|
|
|
|
: createUri(serverInfo.notebookServerEndpoint, "public/generateSQLQuery");
|
2023-10-09 12:42:25 -05:00
|
|
|
|
const response = await fetch(queryUri, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"content-type": "application/json",
|
|
|
|
|
"x-ms-correlationid": useQueryCopilot.getState().correlationId,
|
2023-11-09 11:55:25 -06:00
|
|
|
|
Authorization: `token ${useQueryCopilot.getState().notebookServerInfo.authToken}`,
|
2023-10-09 12:42:25 -05:00
|
|
|
|
},
|
|
|
|
|
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();
|
2023-11-09 11:55:25 -06:00
|
|
|
|
TelemetryProcessor.traceSuccess(Action.QueryGenerationFromCopilotPrompt, {
|
|
|
|
|
databaseName: databaseId,
|
|
|
|
|
collectionId: containerId,
|
|
|
|
|
copilotLatency:
|
|
|
|
|
Date.parse(generateSQLQueryResponse?.generateEnd) - Date.parse(generateSQLQueryResponse?.generateStart),
|
|
|
|
|
responseCode: response.status,
|
|
|
|
|
});
|
2023-10-09 12:42:25 -05:00
|
|
|
|
} else {
|
|
|
|
|
setShowInvalidQueryMessageBar(true);
|
2023-11-09 11:55:25 -06:00
|
|
|
|
TelemetryProcessor.traceFailure(Action.QueryGenerationFromCopilotPrompt, {
|
|
|
|
|
databaseName: databaseId,
|
|
|
|
|
collectionId: containerId,
|
|
|
|
|
responseCode: response.status,
|
|
|
|
|
});
|
2023-10-09 12:42:25 -05:00
|
|
|
|
}
|
2023-11-10 16:55:39 -06:00
|
|
|
|
} else if (response?.status === HttpStatusCodes.TooManyRequests) {
|
|
|
|
|
handleError(JSON.stringify(generateSQLQueryResponse), "copilotTooManyRequestError");
|
|
|
|
|
useTabs.getState().setIsQueryErrorThrown(true);
|
|
|
|
|
setShowErrorMessageBar(true);
|
|
|
|
|
setErrorMessage("Ratelimit exceeded 5 per 1 minute. Please try again after sometime");
|
|
|
|
|
TelemetryProcessor.traceFailure(Action.QueryGenerationFromCopilotPrompt, {
|
|
|
|
|
databaseName: databaseId,
|
|
|
|
|
collectionId: containerId,
|
|
|
|
|
responseCode: response.status,
|
|
|
|
|
});
|
2023-10-09 12:42:25 -05:00
|
|
|
|
} else {
|
|
|
|
|
handleError(JSON.stringify(generateSQLQueryResponse), "copilotInternalServerError");
|
|
|
|
|
useTabs.getState().setIsQueryErrorThrown(true);
|
|
|
|
|
setShowErrorMessageBar(true);
|
2023-11-09 11:55:25 -06:00
|
|
|
|
TelemetryProcessor.traceFailure(Action.QueryGenerationFromCopilotPrompt, {
|
|
|
|
|
databaseName: databaseId,
|
|
|
|
|
collectionId: containerId,
|
|
|
|
|
responseCode: response.status,
|
|
|
|
|
});
|
2023-10-09 12:42:25 -05:00
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
handleError(error, "executeNaturalLanguageQuery");
|
|
|
|
|
useTabs.getState().setIsQueryErrorThrown(true);
|
|
|
|
|
setShowErrorMessageBar(true);
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
setIsGeneratingQuery(false);
|
|
|
|
|
useTabs.getState().setIsTabExecuting(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const showTeachingBubble = (): void => {
|
2024-01-19 09:37:17 -06:00
|
|
|
|
if (showPromptTeachingBubble && !inputEdited.current) {
|
2023-10-09 12:42:25 -05:00
|
|
|
|
setTimeout(() => {
|
2023-10-12 21:54:01 -05:00
|
|
|
|
if (!inputEdited.current && !isWelcomModalVisible()) {
|
2024-01-19 09:37:17 -06:00
|
|
|
|
setCopilotTeachingBubbleVisible(true);
|
2023-10-09 12:42:25 -05:00
|
|
|
|
inputEdited.current = true;
|
|
|
|
|
}
|
|
|
|
|
}, 30000);
|
2024-01-19 09:37:17 -06:00
|
|
|
|
} else {
|
|
|
|
|
toggleCopilotTeachingBubbleVisible(false);
|
2023-10-09 12:42:25 -05:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-19 09:37:17 -06:00
|
|
|
|
const toggleCopilotTeachingBubbleVisible = (visible: boolean): void => {
|
|
|
|
|
setCopilotTeachingBubbleVisible(visible);
|
|
|
|
|
setShowPromptTeachingBubble(visible);
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-12 21:54:01 -05:00
|
|
|
|
const isWelcomModalVisible = (): boolean => {
|
|
|
|
|
return localStorage.getItem("hideWelcomeModal") !== "true";
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-09 12:42:25 -05:00
|
|
|
|
const clearFeedback = () => {
|
|
|
|
|
resetButtonState();
|
|
|
|
|
resetQueryResults();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const resetButtonState = () => {
|
|
|
|
|
setDislikeQuery(false);
|
|
|
|
|
setLikeQuery(false);
|
|
|
|
|
setShowCallout(false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const startGenerateQueryProcess = () => {
|
|
|
|
|
updateHistories();
|
|
|
|
|
generateSQLQuery();
|
|
|
|
|
resetButtonState();
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-04 19:37:16 +05:30
|
|
|
|
const getAriaLabel = () => {
|
|
|
|
|
if (isGeneratingQuery === null) {
|
|
|
|
|
return " ";
|
|
|
|
|
} else if (isGeneratingQuery) {
|
|
|
|
|
return "Content is loading";
|
|
|
|
|
} else {
|
|
|
|
|
return "Content is updated";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-10-09 12:42:25 -05:00
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
showTeachingBubble();
|
|
|
|
|
useTabs.getState().setIsQueryErrorThrown(false);
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
return (
|
2024-01-04 19:37:35 +05:30
|
|
|
|
<Stack
|
|
|
|
|
className="copilot-prompt-pane"
|
|
|
|
|
styles={{ root: { backgroundColor: "#FAFAFA", padding: "16px 24px 0px" } }}
|
|
|
|
|
id="copilot-textfield-label"
|
|
|
|
|
>
|
2023-10-09 12:42:25 -05:00
|
|
|
|
<Stack horizontal>
|
2024-01-04 19:37:50 +05:30
|
|
|
|
<Image src={CopilotIcon} style={{ width: 24, height: 24 }} alt="Copilot" role="none" />
|
2023-10-09 12:42:25 -05:00
|
|
|
|
<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"
|
2024-01-19 09:37:17 -06:00
|
|
|
|
title="Close copilot"
|
2023-10-09 12:42:25 -05:00
|
|
|
|
/>
|
|
|
|
|
</Stack>
|
|
|
|
|
<Stack horizontal verticalAlign="center">
|
|
|
|
|
<TextField
|
|
|
|
|
id="naturalLanguageInput"
|
|
|
|
|
value={userPrompt}
|
|
|
|
|
onChange={handleUserPromptChange}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
inputEdited.current = true;
|
|
|
|
|
setShowSamplePrompts(true);
|
|
|
|
|
}}
|
|
|
|
|
onKeyDown={(e) => {
|
2023-10-12 21:54:01 -05:00
|
|
|
|
if (e.key === "Enter" && userPrompt) {
|
2023-10-09 12:42:25 -05:00
|
|
|
|
inputEdited.current = true;
|
|
|
|
|
startGenerateQueryProcess();
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
style={{ lineHeight: 30 }}
|
|
|
|
|
styles={{ root: { width: "95%" }, fieldGroup: { borderRadius: 6 } }}
|
|
|
|
|
disabled={isGeneratingQuery}
|
|
|
|
|
autoComplete="off"
|
2023-11-09 11:55:25 -06:00
|
|
|
|
placeholder="Ask a question in natural language and we’ll generate the query for you."
|
2024-01-04 19:37:35 +05:30
|
|
|
|
aria-labelledby="copilot-textfield-label"
|
2023-10-09 12:42:25 -05:00
|
|
|
|
/>
|
2024-01-19 09:37:17 -06:00
|
|
|
|
{showPromptTeachingBubble && copilotTeachingBubbleVisible && (
|
2023-10-09 12:42:25 -05:00
|
|
|
|
<TeachingBubble
|
|
|
|
|
calloutProps={{ directionalHint: DirectionalHint.bottomCenter }}
|
|
|
|
|
target="#naturalLanguageInput"
|
|
|
|
|
hasCloseButton={true}
|
|
|
|
|
closeButtonAriaLabel="Close"
|
2024-01-19 09:37:17 -06:00
|
|
|
|
onDismiss={() => toggleCopilotTeachingBubbleVisible(false)}
|
2023-10-09 12:42:25 -05:00
|
|
|
|
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);
|
2024-01-19 09:37:17 -06:00
|
|
|
|
toggleCopilotTeachingBubbleVisible(false);
|
2023-10-09 12:42:25 -05:00
|
|
|
|
}}
|
|
|
|
|
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()}
|
2024-01-04 19:35:12 +05:30
|
|
|
|
aria-label="Send"
|
2023-10-09 12:42:25 -05:00
|
|
|
|
/>
|
2024-01-04 19:37:16 +05:30
|
|
|
|
<div role="alert" aria-label={getAriaLabel()}>
|
|
|
|
|
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
|
|
|
|
</div>
|
2023-10-09 12:42:25 -05:00
|
|
|
|
{showSamplePrompts && (
|
|
|
|
|
<Callout
|
2023-11-10 16:55:39 -06:00
|
|
|
|
styles={{ root: { minWidth: 400, maxWidth: "70vw" } }}
|
2023-10-09 12:42:25 -05:00
|
|
|
|
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;
|
|
|
|
|
}}
|
2023-11-10 16:55:39 -06:00
|
|
|
|
onRenderIcon={() => <Image src={RecentIcon} styles={{ root: { overflow: "unset" } }} />}
|
2023-10-09 12:42:25 -05:00
|
|
|
|
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.{" "}
|
2024-01-04 19:34:58 +05:30
|
|
|
|
<Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank" style={{ color: "#0072c9" }}>
|
2023-10-09 12:42:25 -05:00
|
|
|
|
Read preview terms
|
|
|
|
|
</Link>
|
|
|
|
|
{showErrorMessageBar && (
|
|
|
|
|
<MessageBar messageBarType={MessageBarType.error}>
|
2023-11-10 16:55:39 -06:00
|
|
|
|
{errorMessage ? errorMessage : "We ran into an error and were not able to execute query."}
|
2023-10-09 12:42:25 -05:00
|
|
|
|
</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
|
2024-01-20 09:17:47 +05:30
|
|
|
|
role="status"
|
2023-10-09 12:42:25 -05:00
|
|
|
|
style={{ padding: 8 }}
|
|
|
|
|
target="#likeBtn"
|
|
|
|
|
onDismiss={() => {
|
|
|
|
|
setShowCallout(false);
|
|
|
|
|
SubmitFeedback({
|
|
|
|
|
params: {
|
|
|
|
|
generatedQuery: generatedQuery,
|
|
|
|
|
likeQuery: likeQuery,
|
|
|
|
|
description: "",
|
|
|
|
|
userPrompt: userPrompt,
|
|
|
|
|
},
|
2023-11-09 11:55:25 -06:00
|
|
|
|
explorer,
|
|
|
|
|
databaseId,
|
|
|
|
|
containerId,
|
|
|
|
|
mode: isSampleCopilotActive ? "Sample" : "User",
|
2023-10-09 12:42:25 -05:00
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
directionalHint={DirectionalHint.topCenter}
|
|
|
|
|
>
|
|
|
|
|
<Text>
|
|
|
|
|
Thank you. Need to give{" "}
|
|
|
|
|
<Link
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setShowCallout(false);
|
2023-11-09 11:55:25 -06:00
|
|
|
|
openFeedbackModal(generatedQuery, true, userPrompt);
|
2023-10-09 12:42:25 -05:00
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
more feedback?
|
|
|
|
|
</Link>
|
|
|
|
|
</Text>
|
|
|
|
|
</Callout>
|
|
|
|
|
)}
|
|
|
|
|
<IconButton
|
|
|
|
|
id="likeBtn"
|
|
|
|
|
style={{ marginLeft: 20 }}
|
2024-01-24 16:46:28 -08:00
|
|
|
|
aria-label="Like"
|
2024-01-20 09:17:47 +05:30
|
|
|
|
role="toggle"
|
2023-10-09 12:42:25 -05:00
|
|
|
|
iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setShowCallout(!likeQuery);
|
|
|
|
|
setLikeQuery(!likeQuery);
|
2024-01-20 09:17:47 +05:30
|
|
|
|
if (likeQuery === true) {
|
|
|
|
|
document.getElementById("likeStatus").innerHTML = "Unpressed";
|
|
|
|
|
}
|
|
|
|
|
if (likeQuery === false) {
|
|
|
|
|
document.getElementById("likeStatus").innerHTML = "Liked";
|
|
|
|
|
}
|
2023-10-09 12:42:25 -05:00
|
|
|
|
if (dislikeQuery) {
|
|
|
|
|
setDislikeQuery(!dislikeQuery);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<IconButton
|
|
|
|
|
style={{ margin: "0 10px" }}
|
2024-01-20 09:17:47 +05:30
|
|
|
|
role="toggle"
|
|
|
|
|
aria-label="Dislike"
|
2023-10-09 12:42:25 -05:00
|
|
|
|
iconProps={{ iconName: dislikeQuery === true ? "DislikeSolid" : "Dislike" }}
|
|
|
|
|
onClick={() => {
|
2024-01-20 09:17:47 +05:30
|
|
|
|
let toggleStatusValue = "Unpressed";
|
2023-10-09 12:42:25 -05:00
|
|
|
|
if (!dislikeQuery) {
|
2023-11-09 11:55:25 -06:00
|
|
|
|
openFeedbackModal(generatedQuery, false, userPrompt);
|
2023-10-09 12:42:25 -05:00
|
|
|
|
setLikeQuery(false);
|
2024-01-20 09:17:47 +05:30
|
|
|
|
toggleStatusValue = "Disliked";
|
2023-10-09 12:42:25 -05:00
|
|
|
|
}
|
|
|
|
|
setDislikeQuery(!dislikeQuery);
|
|
|
|
|
setShowCallout(false);
|
2024-01-20 09:17:47 +05:30
|
|
|
|
document.getElementById("likeStatus").innerHTML = toggleStatusValue;
|
2023-10-09 12:42:25 -05:00
|
|
|
|
}}
|
|
|
|
|
/>
|
2024-01-20 09:17:47 +05:30
|
|
|
|
|
|
|
|
|
<span role="status" style={{ position: "absolute", left: "-9999px" }} id="likeStatus"></span>
|
|
|
|
|
|
2023-10-09 12:42:25 -05:00
|
|
|
|
<Separator vertical style={{ color: "#EDEBE9" }} />
|
|
|
|
|
<CommandBarButton
|
|
|
|
|
onClick={copyGeneratedCode}
|
|
|
|
|
iconProps={{ iconName: "Copy" }}
|
|
|
|
|
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
|
|
|
|
>
|
2023-10-12 21:54:01 -05:00
|
|
|
|
Copy query
|
2023-10-09 12:42:25 -05:00
|
|
|
|
</CommandBarButton>
|
|
|
|
|
<CommandBarButton
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setShowDeletePopup(true);
|
|
|
|
|
}}
|
|
|
|
|
iconProps={{ iconName: "Delete" }}
|
|
|
|
|
style={{ margin: "0 10px", backgroundColor: "#FFF8F0", transition: "background-color 0.3s ease" }}
|
|
|
|
|
>
|
2023-10-12 21:54:01 -05:00
|
|
|
|
Delete query
|
2023-10-09 12:42:25 -05:00
|
|
|
|
</CommandBarButton>
|
|
|
|
|
</Stack>
|
|
|
|
|
)}
|
2023-10-12 21:54:01 -05:00
|
|
|
|
<WelcomeModal visible={isWelcomModalVisible()} />
|
2023-10-09 12:42:25 -05:00
|
|
|
|
{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>
|
|
|
|
|
);
|
|
|
|
|
};
|