mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 06:56:38 +00:00
[Query Copilot V2] Restructure code for V2 of copilot (#1577)
* Initial folder and code restructure * Add snapshots * Remove flag for local testing * Remove unnecessary code * Fix snapshot * Update tests
This commit is contained in:
parent
96b88ac344
commit
ceb66ed5b8
@ -32,7 +32,7 @@ import { WelcomeModal } from "Explorer/QueryCopilot/Modal/WelcomeModal";
|
||||
import { CopyPopup } from "Explorer/QueryCopilot/Popup/CopyPopup";
|
||||
import { DeletePopup } from "Explorer/QueryCopilot/Popup/DeletePopup";
|
||||
import { querySampleDocuments, submitFeedback } from "Explorer/QueryCopilot/QueryCopilotUtilities";
|
||||
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/SamplePrompts/SamplePrompts";
|
||||
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/Shared/SamplePrompts/SamplePrompts";
|
||||
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceFailure, traceStart, traceSuccess } from "Shared/Telemetry/TelemetryProcessor";
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { DefaultButton, FontIcon, IconButton, Image, Modal, Stack, Text } from "@fluentui/react";
|
||||
import React, { Dispatch, SetStateAction } from "react";
|
||||
import ComplexPrompts from "../../../../images/ComplexPrompts.svg";
|
||||
import IntermediatePrompts from "../../../../images/IntermediatePrompts.svg";
|
||||
import SimplePrompts from "../../../../images/SimplePrompts.svg";
|
||||
import ComplexPrompts from "../../../../../images/ComplexPrompts.svg";
|
||||
import IntermediatePrompts from "../../../../../images/IntermediatePrompts.svg";
|
||||
import SimplePrompts from "../../../../../images/SimplePrompts.svg";
|
||||
|
||||
export interface SamplePromptsProps {
|
||||
isSamplePromptsOpen: boolean;
|
@ -1,243 +0,0 @@
|
||||
import { IButtonStyles, IconButton, Image, Stack, Text, TextField } from "@fluentui/react";
|
||||
import { SamplePrompts, SamplePromptsProps } from "Explorer/QueryCopilot/SamplePrompts/SamplePrompts";
|
||||
import { WelcomeSidebarPopup } from "Explorer/QueryCopilot/Sidebar/WelcomeSidebarPopup";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import CopilotIcon from "../../../../images/CopilotSidebarLogo.svg";
|
||||
import HintIcon from "../../../../images/Hint.svg";
|
||||
|
||||
const sampleChatMessages: string[] = [
|
||||
"Write a query to return last 10 records in the database",
|
||||
'Write a query to return all records in this table created in the last thirty days which also have the record owner as "Contoso"',
|
||||
];
|
||||
|
||||
const promptStyles: IButtonStyles = {
|
||||
root: { border: "5px", selectors: { ":hover": { outline: "1px dashed #605e5c" } } },
|
||||
label: { fontWeight: 400, textAlign: "left", paddingLeft: 8 },
|
||||
};
|
||||
|
||||
export const QueryCopilotSidebar: React.FC = (): JSX.Element => {
|
||||
const {
|
||||
setWasCopilotUsed,
|
||||
showCopilotSidebar,
|
||||
setShowCopilotSidebar,
|
||||
userPrompt,
|
||||
setUserPrompt,
|
||||
chatMessages,
|
||||
setChatMessages,
|
||||
showWelcomeSidebar,
|
||||
isSamplePromptsOpen,
|
||||
setIsSamplePromptsOpen,
|
||||
} = useQueryCopilot();
|
||||
|
||||
const sampleProps: SamplePromptsProps = {
|
||||
isSamplePromptsOpen: isSamplePromptsOpen,
|
||||
setIsSamplePromptsOpen: setIsSamplePromptsOpen,
|
||||
setTextBox: setUserPrompt,
|
||||
};
|
||||
|
||||
const handleSendMessage = () => {
|
||||
if (userPrompt.trim() !== "") {
|
||||
setChatMessages([...chatMessages, userPrompt]);
|
||||
setUserPrompt("");
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (value: string) => {
|
||||
setUserPrompt(value);
|
||||
};
|
||||
|
||||
const handleEnterKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
handleSendMessage();
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (showCopilotSidebar) {
|
||||
setWasCopilotUsed(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack style={{ width: "100%", height: "100%", backgroundColor: "#FAFAFA", overflow: "auto" }}>
|
||||
<Stack style={{ margin: "15px 0px 0px 0px", padding: "5px" }}>
|
||||
<Stack style={{ display: "flex", justifyContent: "space-between" }} horizontal verticalAlign="center">
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<Image src={CopilotIcon} />
|
||||
<Text style={{ marginLeft: "5px", fontWeight: "bold" }}>Copilot</Text>
|
||||
<Text
|
||||
style={{
|
||||
background: "#f0f0f0",
|
||||
fontSize: "10px",
|
||||
padding: "2px 4px",
|
||||
marginLeft: "5px",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</Stack>
|
||||
<IconButton
|
||||
onClick={() => setShowCopilotSidebar(false)}
|
||||
iconProps={{ iconName: "Cancel" }}
|
||||
title="Exit"
|
||||
ariaLabel="Exit"
|
||||
style={{ color: "#424242", verticalAlign: "middle" }}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{showWelcomeSidebar ? (
|
||||
<Stack.Item styles={{ root: { textAlign: "center", verticalAlign: "middle" } }}>
|
||||
<WelcomeSidebarPopup />
|
||||
</Stack.Item>
|
||||
) : (
|
||||
<>
|
||||
<Stack horizontalAlign="center" style={{ color: "#707070" }} tokens={{ padding: 8, childrenGap: 8 }}>
|
||||
{new Date().toLocaleDateString("en-US", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}{" "}
|
||||
{new Date().toLocaleTimeString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
hour12: true,
|
||||
})}
|
||||
</Stack>
|
||||
<Stack style={{ flexGrow: 1, display: "flex", flexDirection: "column" }}>
|
||||
<div style={{ flexGrow: 1, overflowY: "auto" }}>
|
||||
<Stack
|
||||
tokens={{ padding: 16, childrenGap: 12 }}
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
margin: "5px 10px",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
<Text variant="medium">
|
||||
Hello, I am Cosmos Db's copilot assistant. I can help you do the following things:
|
||||
</Text>
|
||||
<Stack tokens={{ childrenGap: 8 }}>
|
||||
<Stack horizontal style={{ marginLeft: "15px" }} tokens={{ childrenGap: 8 }}>
|
||||
<Text style={{ fontSize: "16px", lineHeight: "16px", verticalAlign: "middle" }}>•</Text>
|
||||
<Text style={{ verticalAlign: "middle" }}>Generate queries based upon prompt you suggest</Text>
|
||||
</Stack>
|
||||
<Stack horizontal style={{ marginLeft: "15px" }} tokens={{ childrenGap: 8 }}>
|
||||
<Text style={{ fontSize: "16px", lineHeight: "16px", verticalAlign: "middle" }}>•</Text>
|
||||
<Text style={{ verticalAlign: "middle" }}>
|
||||
Explain and provide alternate queries for a query suggested by you
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack horizontal style={{ marginLeft: "15px" }} tokens={{ childrenGap: 8 }}>
|
||||
<Text style={{ fontSize: "16px", lineHeight: "16px", verticalAlign: "middle" }}>•</Text>
|
||||
<Text style={{ verticalAlign: "middle" }}>Help answer questions about Cosmos DB</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Text variant="medium">
|
||||
To get started, ask me a question or use one of the sample prompts to generate a query. AI-generated
|
||||
content may be incorrect.
|
||||
</Text>
|
||||
</Stack>
|
||||
{chatMessages.map((message, index) => (
|
||||
<Stack
|
||||
key={index}
|
||||
horizontalAlign="center"
|
||||
tokens={{ padding: 8, childrenGap: 8 }}
|
||||
style={{
|
||||
backgroundColor: "#E0E7FF",
|
||||
borderRadius: "8px",
|
||||
margin: "5px 10px",
|
||||
textAlign: "start",
|
||||
}}
|
||||
>
|
||||
{message}
|
||||
</Stack>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{chatMessages.length === 0 && (
|
||||
<Stack
|
||||
horizontalAlign="end"
|
||||
verticalAlign="end"
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "10px",
|
||||
margin: "10px",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
onClick={() => handleInputChange(sampleChatMessages[0])}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
border: "1.5px solid #B0BEFF",
|
||||
width: "100%",
|
||||
padding: "2px",
|
||||
borderRadius: "4px",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
{sampleChatMessages[0]}
|
||||
</Text>
|
||||
<Text
|
||||
onClick={() => handleInputChange(sampleChatMessages[1])}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
border: "1.5px solid #B0BEFF",
|
||||
width: "100%",
|
||||
padding: "2px",
|
||||
borderRadius: "4px",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
{sampleChatMessages[1]}
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<Stack
|
||||
horizontal
|
||||
horizontalAlign="end"
|
||||
verticalAlign="end"
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
borderRadius: "20px",
|
||||
background: "white",
|
||||
padding: "5px",
|
||||
margin: "5px",
|
||||
}}
|
||||
>
|
||||
<Stack>
|
||||
<Image src={HintIcon} styles={promptStyles} onClick={() => setIsSamplePromptsOpen(true)} />
|
||||
<SamplePrompts sampleProps={sampleProps} />
|
||||
</Stack>
|
||||
<TextField
|
||||
placeholder="Write your own prompt or ask a question"
|
||||
value={userPrompt}
|
||||
onChange={(_, newValue) => handleInputChange(newValue)}
|
||||
onKeyDown={handleEnterKeyPress}
|
||||
multiline
|
||||
resizable={false}
|
||||
styles={{
|
||||
root: {
|
||||
width: "100%",
|
||||
height: "80px",
|
||||
borderRadius: "20px",
|
||||
padding: "8px",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
marginLeft: "10px",
|
||||
},
|
||||
fieldGroup: { border: "none" },
|
||||
}}
|
||||
/>
|
||||
<IconButton iconProps={{ iconName: "Send" }} onClick={handleSendMessage} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
@ -0,0 +1,10 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { SampleBubble } from "./SampleBubble";
|
||||
|
||||
describe("Sample Bubble snapshot test", () => {
|
||||
it("should render ", () => {
|
||||
const wrapper = shallow(<SampleBubble />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
42
src/Explorer/QueryCopilot/V2/Bubbles/Sample/SampleBubble.tsx
Normal file
42
src/Explorer/QueryCopilot/V2/Bubbles/Sample/SampleBubble.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { Stack, Text } from "@fluentui/react";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
|
||||
export const SampleBubble: React.FC = (): JSX.Element => {
|
||||
const { setUserPrompt } = useQueryCopilot();
|
||||
|
||||
const sampleChatMessages: string[] = [
|
||||
"Write a query to return last 10 records in the database",
|
||||
'Write a query to return all records in this table created in the last thirty days which also have the record owner as "Contoso"',
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack
|
||||
horizontalAlign="end"
|
||||
verticalAlign="end"
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: "10px",
|
||||
margin: "10px",
|
||||
}}
|
||||
>
|
||||
{sampleChatMessages.map((text, index) => (
|
||||
<Text
|
||||
key={index}
|
||||
onClick={() => setUserPrompt(text)}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
border: "1.5px solid #B0BEFF",
|
||||
width: "100%",
|
||||
padding: "2px",
|
||||
borderRadius: "4px",
|
||||
marginBottom: "5px",
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
@ -0,0 +1,49 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Sample Bubble snapshot test should render 1`] = `
|
||||
<Stack
|
||||
horizontalAlign="end"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"display": "flex",
|
||||
"margin": "10px",
|
||||
"padding": "10px",
|
||||
}
|
||||
}
|
||||
verticalAlign="end"
|
||||
>
|
||||
<Text
|
||||
key="0"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"border": "1.5px solid #B0BEFF",
|
||||
"borderRadius": "4px",
|
||||
"cursor": "pointer",
|
||||
"marginBottom": "5px",
|
||||
"padding": "2px",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
>
|
||||
Write a query to return last 10 records in the database
|
||||
</Text>
|
||||
<Text
|
||||
key="1"
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"border": "1.5px solid #B0BEFF",
|
||||
"borderRadius": "4px",
|
||||
"cursor": "pointer",
|
||||
"marginBottom": "5px",
|
||||
"padding": "2px",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
>
|
||||
Write a query to return all records in this table created in the last thirty days which also have the record owner as "Contoso"
|
||||
</Text>
|
||||
</Stack>
|
||||
`;
|
@ -0,0 +1,13 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { WelcomeBubble } from "./WelcomeBubble";
|
||||
|
||||
const mockedDate = new Date(2023, 7, 15);
|
||||
jest.spyOn(global, "Date").mockImplementation(() => (mockedDate as unknown) as string);
|
||||
|
||||
describe("Welcome Bubble snapshot test", () => {
|
||||
it("should render ", () => {
|
||||
const wrapper = shallow(<WelcomeBubble />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -0,0 +1,52 @@
|
||||
import { Stack, Text } from "@fluentui/react";
|
||||
import React from "react";
|
||||
|
||||
export const WelcomeBubble: React.FC = (): JSX.Element => {
|
||||
return (
|
||||
<Stack>
|
||||
<Stack horizontalAlign="center" style={{ color: "#707070" }} tokens={{ padding: 8, childrenGap: 8 }}>
|
||||
{new Date().toLocaleDateString("en-US", {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}{" "}
|
||||
{new Date().toLocaleTimeString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
hour12: true,
|
||||
})}
|
||||
</Stack>
|
||||
<Stack
|
||||
tokens={{ padding: 16, childrenGap: 12 }}
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
margin: "5px 10px",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
<Text variant="medium">
|
||||
Hello, I am Cosmos Db's copilot assistant. I can help you do the following things:
|
||||
</Text>
|
||||
<Stack tokens={{ childrenGap: 8 }}>
|
||||
<Stack horizontal style={{ marginLeft: "15px" }} tokens={{ childrenGap: 8 }}>
|
||||
<Text style={{ fontSize: "16px", lineHeight: "16px", verticalAlign: "middle" }}>•</Text>
|
||||
<Text style={{ verticalAlign: "middle" }}>Generate queries based upon prompt you suggest</Text>
|
||||
</Stack>
|
||||
<Stack horizontal style={{ marginLeft: "15px" }} tokens={{ childrenGap: 8 }}>
|
||||
<Text style={{ fontSize: "16px", lineHeight: "16px", verticalAlign: "middle" }}>•</Text>
|
||||
<Text style={{ verticalAlign: "middle" }}>
|
||||
Explain and provide alternate queries for a query suggested by you
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack horizontal style={{ marginLeft: "15px" }} tokens={{ childrenGap: 8 }}>
|
||||
<Text style={{ fontSize: "16px", lineHeight: "16px", verticalAlign: "middle" }}>•</Text>
|
||||
<Text style={{ verticalAlign: "middle" }}>Help answer questions about Cosmos DB</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Text variant="medium">
|
||||
To get started, ask me a question or use one of the sample prompts to generate a query. AI-generated content
|
||||
may be incorrect.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
};
|
@ -0,0 +1,160 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Welcome Bubble snapshot test should render 1`] = `
|
||||
<Stack>
|
||||
<Stack
|
||||
horizontalAlign="center"
|
||||
style={
|
||||
Object {
|
||||
"color": "#707070",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 8,
|
||||
"padding": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
August 15
|
||||
|
||||
12:00 AM
|
||||
</Stack>
|
||||
<Stack
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "white",
|
||||
"borderRadius": "8px",
|
||||
"margin": "5px 10px",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 12,
|
||||
"padding": 16,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
variant="medium"
|
||||
>
|
||||
Hello, I am Cosmos Db's copilot assistant. I can help you do the following things:
|
||||
</Text>
|
||||
<Stack
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "15px",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "16px",
|
||||
"lineHeight": "16px",
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
>
|
||||
•
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
>
|
||||
Generate queries based upon prompt you suggest
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "15px",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "16px",
|
||||
"lineHeight": "16px",
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
>
|
||||
•
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
>
|
||||
Explain and provide alternate queries for a query suggested by you
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "15px",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "16px",
|
||||
"lineHeight": "16px",
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
>
|
||||
•
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
>
|
||||
Help answer questions about Cosmos DB
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Text
|
||||
variant="medium"
|
||||
>
|
||||
To get started, ask me a question or use one of the sample prompts to generate a query. AI-generated content may be incorrect.
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
`;
|
10
src/Explorer/QueryCopilot/V2/Footer/Footer.test.tsx
Normal file
10
src/Explorer/QueryCopilot/V2/Footer/Footer.test.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { Footer } from "./Footer";
|
||||
|
||||
describe("Footer snapshot test", () => {
|
||||
it("should render ", () => {
|
||||
const wrapper = shallow(<Footer />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
83
src/Explorer/QueryCopilot/V2/Footer/Footer.tsx
Normal file
83
src/Explorer/QueryCopilot/V2/Footer/Footer.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { IButtonStyles, IconButton, Image, Stack, TextField } from "@fluentui/react";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import HintIcon from "../../../../../images/Hint.svg";
|
||||
import { SamplePrompts, SamplePromptsProps } from "../../Shared/SamplePrompts/SamplePrompts";
|
||||
|
||||
export const Footer: React.FC = (): JSX.Element => {
|
||||
const {
|
||||
userPrompt,
|
||||
setUserPrompt,
|
||||
chatMessages,
|
||||
setChatMessages,
|
||||
isSamplePromptsOpen,
|
||||
setIsSamplePromptsOpen,
|
||||
} = useQueryCopilot();
|
||||
|
||||
const promptStyles: IButtonStyles = {
|
||||
root: { border: "5px", selectors: { ":hover": { outline: "1px dashed #605e5c" } } },
|
||||
label: { fontWeight: 400, textAlign: "left", paddingLeft: 8 },
|
||||
};
|
||||
|
||||
const sampleProps: SamplePromptsProps = {
|
||||
isSamplePromptsOpen: isSamplePromptsOpen,
|
||||
setIsSamplePromptsOpen: setIsSamplePromptsOpen,
|
||||
setTextBox: setUserPrompt,
|
||||
};
|
||||
|
||||
const handleEnterKeyPress = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
handleSendMessage();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSendMessage = () => {
|
||||
if (userPrompt.trim() !== "") {
|
||||
setChatMessages([...chatMessages, userPrompt]);
|
||||
setUserPrompt("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack
|
||||
horizontal
|
||||
horizontalAlign="end"
|
||||
verticalAlign="end"
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
borderRadius: "20px",
|
||||
background: "white",
|
||||
padding: "5px",
|
||||
margin: "5px",
|
||||
}}
|
||||
>
|
||||
<Stack>
|
||||
<Image src={HintIcon} styles={promptStyles} onClick={() => setIsSamplePromptsOpen(true)} />
|
||||
<SamplePrompts sampleProps={sampleProps} />
|
||||
</Stack>
|
||||
<TextField
|
||||
placeholder="Write your own prompt or ask a question"
|
||||
value={userPrompt}
|
||||
onChange={(_, newValue) => setUserPrompt(newValue)}
|
||||
onKeyDown={handleEnterKeyPress}
|
||||
multiline
|
||||
resizable={false}
|
||||
styles={{
|
||||
root: {
|
||||
width: "100%",
|
||||
height: "80px",
|
||||
borderRadius: "20px",
|
||||
padding: "8px",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
marginLeft: "10px",
|
||||
},
|
||||
fieldGroup: { border: "none" },
|
||||
}}
|
||||
/>
|
||||
<IconButton iconProps={{ iconName: "Send" }} onClick={handleSendMessage} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
@ -0,0 +1,84 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Footer snapshot test should render 1`] = `
|
||||
<Stack
|
||||
horizontal={true}
|
||||
horizontalAlign="end"
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"background": "white",
|
||||
"borderRadius": "20px",
|
||||
"display": "flex",
|
||||
"margin": "5px",
|
||||
"padding": "5px",
|
||||
}
|
||||
}
|
||||
verticalAlign="end"
|
||||
>
|
||||
<Stack>
|
||||
<Image
|
||||
onClick={[Function]}
|
||||
src=""
|
||||
styles={
|
||||
Object {
|
||||
"label": Object {
|
||||
"fontWeight": 400,
|
||||
"paddingLeft": 8,
|
||||
"textAlign": "left",
|
||||
},
|
||||
"root": Object {
|
||||
"border": "5px",
|
||||
"selectors": Object {
|
||||
":hover": Object {
|
||||
"outline": "1px dashed #605e5c",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
/>
|
||||
<SamplePrompts
|
||||
sampleProps={
|
||||
Object {
|
||||
"isSamplePromptsOpen": false,
|
||||
"setIsSamplePromptsOpen": [Function],
|
||||
"setTextBox": [Function],
|
||||
}
|
||||
}
|
||||
/>
|
||||
</Stack>
|
||||
<StyledTextFieldBase
|
||||
multiline={true}
|
||||
onChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
placeholder="Write your own prompt or ask a question"
|
||||
resizable={false}
|
||||
styles={
|
||||
Object {
|
||||
"fieldGroup": Object {
|
||||
"border": "none",
|
||||
},
|
||||
"root": Object {
|
||||
"border": "none",
|
||||
"borderRadius": "20px",
|
||||
"height": "80px",
|
||||
"marginLeft": "10px",
|
||||
"outline": "none",
|
||||
"padding": "8px",
|
||||
"width": "100%",
|
||||
},
|
||||
}
|
||||
}
|
||||
value=""
|
||||
/>
|
||||
<CustomizedIconButton
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "Send",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</Stack>
|
||||
`;
|
10
src/Explorer/QueryCopilot/V2/Header/Header.test.tsx
Normal file
10
src/Explorer/QueryCopilot/V2/Header/Header.test.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { Header } from "./Header";
|
||||
|
||||
describe("Header snapshot test", () => {
|
||||
it("should render ", () => {
|
||||
const wrapper = shallow(<Header />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
39
src/Explorer/QueryCopilot/V2/Header/Header.tsx
Normal file
39
src/Explorer/QueryCopilot/V2/Header/Header.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { IconButton, Image, Stack, Text } from "@fluentui/react";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import CopilotIcon from "../../../../../images/CopilotSidebarLogo.svg";
|
||||
|
||||
export const Header: React.FC = (): JSX.Element => {
|
||||
const { setShowCopilotSidebar } = useQueryCopilot();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
style={{ margin: "15px 0px 0px 0px", padding: "5px", display: "flex", justifyContent: "space-between" }}
|
||||
horizontal
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Stack horizontal verticalAlign="center">
|
||||
<Image src={CopilotIcon} />
|
||||
<Text style={{ marginLeft: "5px", fontWeight: "bold" }}>Copilot</Text>
|
||||
<Text
|
||||
style={{
|
||||
background: "#f0f0f0",
|
||||
fontSize: "10px",
|
||||
padding: "2px 4px",
|
||||
marginLeft: "5px",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</Stack>
|
||||
<IconButton
|
||||
onClick={() => setShowCopilotSidebar(false)}
|
||||
iconProps={{ iconName: "Cancel" }}
|
||||
title="Exit"
|
||||
ariaLabel="Exit"
|
||||
style={{ color: "#424242", verticalAlign: "middle" }}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
@ -0,0 +1,64 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Header snapshot test should render 1`] = `
|
||||
<Stack
|
||||
horizontal={true}
|
||||
style={
|
||||
Object {
|
||||
"display": "flex",
|
||||
"justifyContent": "space-between",
|
||||
"margin": "15px 0px 0px 0px",
|
||||
"padding": "5px",
|
||||
}
|
||||
}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Image
|
||||
src=""
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": "bold",
|
||||
"marginLeft": "5px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Copilot
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"background": "#f0f0f0",
|
||||
"borderRadius": "8px",
|
||||
"fontSize": "10px",
|
||||
"marginLeft": "5px",
|
||||
"padding": "2px 4px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Preview
|
||||
</Text>
|
||||
</Stack>
|
||||
<CustomizedIconButton
|
||||
ariaLabel="Exit"
|
||||
iconProps={
|
||||
Object {
|
||||
"iconName": "Cancel",
|
||||
}
|
||||
}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "#424242",
|
||||
"verticalAlign": "middle",
|
||||
}
|
||||
}
|
||||
title="Exit"
|
||||
/>
|
||||
</Stack>
|
||||
`;
|
@ -0,0 +1,10 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { WelcomeSidebarModal } from "./WelcomeSidebarModal";
|
||||
|
||||
describe("Footer snapshot test", () => {
|
||||
it("should render ", () => {
|
||||
const wrapper = shallow(<WelcomeSidebarModal />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -1,12 +1,12 @@
|
||||
import { Image, Link, PrimaryButton, Stack, Text } from "@fluentui/react";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import Database from "../../../../images/CopilotDatabase.svg";
|
||||
import Flash from "../../../../images/CopilotFlash.svg";
|
||||
import CopilotSidebarWelcomeIllustration from "../../../../images/CopilotSidebarWelcomeIllustration.svg";
|
||||
import Thumb from "../../../../images/CopilotThumb.svg";
|
||||
import Database from "../../../../../images/CopilotDatabase.svg";
|
||||
import Flash from "../../../../../images/CopilotFlash.svg";
|
||||
import CopilotSidebarWelcomeIllustration from "../../../../../images/CopilotSidebarWelcomeIllustration.svg";
|
||||
import Thumb from "../../../../../images/CopilotThumb.svg";
|
||||
|
||||
export const WelcomeSidebarPopup: React.FC = (): JSX.Element => {
|
||||
export const WelcomeSidebarModal: React.FC = (): JSX.Element => {
|
||||
const { setShowWelcomeSidebar } = useQueryCopilot();
|
||||
|
||||
const hideModal = () => {
|
@ -0,0 +1,243 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Footer snapshot test should render 1`] = `
|
||||
<Stack
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#FAFAFA",
|
||||
"height": "100%",
|
||||
"overflow": "auto",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "white",
|
||||
"borderRadius": "20px",
|
||||
"boxSizing": "border-box",
|
||||
"margin": "20px 10px",
|
||||
"maxHeight": "100%",
|
||||
"padding": "20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
horizontalAlign="center"
|
||||
verticalAlign="center"
|
||||
>
|
||||
<Image
|
||||
src=""
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<StackItem
|
||||
align="center"
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "10px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
className="title bold"
|
||||
>
|
||||
Welcome to Copilot in CosmosDB
|
||||
</Text>
|
||||
</StackItem>
|
||||
<StackItem
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "15px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<StackItem
|
||||
align="start"
|
||||
>
|
||||
<Image
|
||||
src=""
|
||||
/>
|
||||
</StackItem>
|
||||
<StackItem
|
||||
align="center"
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "10px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": 600,
|
||||
}
|
||||
}
|
||||
>
|
||||
Let copilot do the work for you
|
||||
<br />
|
||||
</Text>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<StackItem
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "25px",
|
||||
"textAlign": "start",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
Ask Copilot to generate a query by describing the query in your words.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href="http://aka.ms/cdb-copilot-learn-more"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
</StackItem>
|
||||
<StackItem
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "15px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<StackItem
|
||||
align="start"
|
||||
>
|
||||
<Image
|
||||
src=""
|
||||
/>
|
||||
</StackItem>
|
||||
<StackItem
|
||||
align="center"
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "10px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": 600,
|
||||
}
|
||||
}
|
||||
>
|
||||
Use your judgement
|
||||
<br />
|
||||
</Text>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<StackItem
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "25px",
|
||||
"textAlign": "start",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
AI-generated content can have mistakes. Make sure it’s accurate and appropriate before using it.
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href="http://aka.ms/cdb-copilot-preview-terms"
|
||||
>
|
||||
Read preview terms
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
</StackItem>
|
||||
<StackItem
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "15px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<StackItem
|
||||
align="start"
|
||||
>
|
||||
<Image
|
||||
src=""
|
||||
/>
|
||||
</StackItem>
|
||||
<StackItem
|
||||
align="center"
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "10px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": 600,
|
||||
}
|
||||
}
|
||||
>
|
||||
Copilot currently works only a sample database
|
||||
<br />
|
||||
</Text>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<StackItem
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "25px",
|
||||
"textAlign": "start",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
Copilot is setup on a sample database we have configured for you at no cost
|
||||
<br />
|
||||
<StyledLinkBase
|
||||
href="http://aka.ms/cdb-copilot-learn-more"
|
||||
>
|
||||
Learn more
|
||||
</StyledLinkBase>
|
||||
</Text>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<StackItem
|
||||
align="center"
|
||||
>
|
||||
<CustomizedPrimaryButton
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": "32px",
|
||||
"width": "224px",
|
||||
}
|
||||
}
|
||||
>
|
||||
Get Started
|
||||
</CustomizedPrimaryButton>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
</div>
|
||||
</Stack>
|
||||
`;
|
@ -0,0 +1,10 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import { QueryCopilotSidebar } from "./QueryCopilotSidebar";
|
||||
|
||||
describe("Footer snapshot test", () => {
|
||||
it("should render ", () => {
|
||||
const wrapper = shallow(<QueryCopilotSidebar />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
57
src/Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar.tsx
Normal file
57
src/Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import { Stack } from "@fluentui/react";
|
||||
import { SampleBubble } from "Explorer/QueryCopilot/V2/Bubbles/Sample/SampleBubble";
|
||||
import { WelcomeBubble } from "Explorer/QueryCopilot/V2/Bubbles/Welcome/WelcomeBubble";
|
||||
import { Footer } from "Explorer/QueryCopilot/V2/Footer/Footer";
|
||||
import { Header } from "Explorer/QueryCopilot/V2/Header/Header";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import React from "react";
|
||||
import { WelcomeSidebarModal } from "../Modal/WelcomeSidebarModal";
|
||||
|
||||
export const QueryCopilotSidebar: React.FC = (): JSX.Element => {
|
||||
const { setWasCopilotUsed, showCopilotSidebar, chatMessages, showWelcomeSidebar } = useQueryCopilot();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (showCopilotSidebar) {
|
||||
setWasCopilotUsed(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack style={{ width: "100%", height: "100%", backgroundColor: "#FAFAFA", overflow: "auto" }}>
|
||||
<Header />
|
||||
{showWelcomeSidebar ? (
|
||||
<WelcomeSidebarModal />
|
||||
) : (
|
||||
<>
|
||||
<Stack
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<WelcomeBubble />
|
||||
{chatMessages.map((message, index) => (
|
||||
<Stack
|
||||
key={index}
|
||||
horizontalAlign="center"
|
||||
tokens={{ padding: 8, childrenGap: 8 }}
|
||||
style={{
|
||||
backgroundColor: "#E0E7FF",
|
||||
borderRadius: "8px",
|
||||
margin: "5px 10px",
|
||||
textAlign: "start",
|
||||
}}
|
||||
>
|
||||
{message}
|
||||
</Stack>
|
||||
))}
|
||||
{chatMessages.length === 0 && <SampleBubble />}
|
||||
</Stack>
|
||||
<Footer />
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Footer snapshot test should render 1`] = `
|
||||
<Stack
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#FAFAFA",
|
||||
"height": "100%",
|
||||
"overflow": "auto",
|
||||
"width": "100%",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Header />
|
||||
<WelcomeSidebarModal />
|
||||
</Stack>
|
||||
`;
|
@ -1,5 +1,5 @@
|
||||
import { FeedOptions } from "@azure/cosmos";
|
||||
import { QueryCopilotSidebar } from "Explorer/QueryCopilot/Sidebar/QueryCopilotSidebar";
|
||||
import { QueryCopilotSidebar } from "Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar";
|
||||
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { QueryCopilotState, useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
|
Loading…
Reference in New Issue
Block a user