Compare commits

..

4 Commits

Author SHA1 Message Date
Sampath
b38f558248 fixes for lint and compilation errors 2024-01-02 23:05:06 +05:30
Sampath
3c9325ecbc fixes for lint and compilation errors 2024-01-02 23:00:35 +05:30
MokireddySampath
309fcd112e Update PanelContainerComponent.tsx 2024-01-02 22:52:22 +05:30
Sampath
eba617f53f focus management has been done for the save query and close button of dialog 2024-01-02 22:34:56 +05:30
42 changed files with 5064 additions and 725 deletions

View File

@@ -2897,21 +2897,9 @@ a:link {
padding-left: 8px; padding-left: 8px;
} }
.settingsSectionInlineCheckbox {
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
align-items: center;
gap: 5px;
}
.settingsSectionLabel { .settingsSectionLabel {
margin-bottom: @DefaultSpace; margin-bottom: @DefaultSpace;
margin-right: 5px; margin-right: 5px;
.panelInfoIcon {
margin-left: 5px;
}
} }
.pageOptionsPart { .pageOptionsPart {

1412
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -55,8 +55,8 @@
"crossroads": "0.12.2", "crossroads": "0.12.2",
"css-element-queries": "1.1.1", "css-element-queries": "1.1.1",
"d3": "6.1.1", "d3": "6.1.1",
"datatables.net-colreorder-dt": "1.7.0", "datatables.net-colreorder-dt": "1.5.1",
"datatables.net-dt": "1.13.8", "datatables.net-dt": "1.10.19",
"date-fns": "1.29.0", "date-fns": "1.29.0",
"dayjs": "1.8.19", "dayjs": "1.8.19",
"dom-to-image": "2.6.0", "dom-to-image": "2.6.0",
@@ -71,14 +71,13 @@
"iframe-resizer-react": "1.1.0", "iframe-resizer-react": "1.1.0",
"immutable": "4.0.0-rc.12", "immutable": "4.0.0-rc.12",
"is-ci": "2.0.0", "is-ci": "2.0.0",
"jquery": "3.7.1", "jquery": "3.5.1",
"jquery-typeahead": "2.11.1", "jquery-typeahead": "2.10.6",
"jquery-ui-dist": "1.13.2", "jquery-ui-dist": "1.12.1",
"knockout": "3.5.1", "knockout": "3.5.1",
"mkdirp": "1.0.4", "mkdirp": "1.0.4",
"monaco-editor": "0.44.0", "monaco-editor": "0.44.0",
"ms": "2.1.3", "ms": "2.1.3",
"patch-package": "8.0.0",
"p-retry": "4.6.2", "p-retry": "4.6.2",
"plotly.js-cartesian-dist-min": "1.52.3", "plotly.js-cartesian-dist-min": "1.52.3",
"post-robot": "10.0.42", "post-robot": "10.0.42",
@@ -115,14 +114,11 @@
"@types/codemirror": "0.0.56", "@types/codemirror": "0.0.56",
"@types/crossroads": "0.0.30", "@types/crossroads": "0.0.30",
"@types/d3": "5.9.2", "@types/d3": "5.9.2",
"@types/datatables.net": "1.10.28",
"@types/datatables.net-colreorder": "1.4.5",
"@types/dom-to-image": "2.6.2", "@types/dom-to-image": "2.6.2",
"@types/enzyme": "3.10.7", "@types/enzyme": "3.10.7",
"@types/enzyme-adapter-react-16": "1.0.6", "@types/enzyme-adapter-react-16": "1.0.6",
"@types/hasher": "0.0.31", "@types/hasher": "0.0.31",
"@types/jest": "26.0.20", "@types/jest": "26.0.20",
"@types/jquery": "3.5.29",
"@types/node": "12.11.1", "@types/node": "12.11.1",
"@types/post-robot": "10.0.1", "@types/post-robot": "10.0.1",
"@types/q": "1.5.1", "@types/q": "1.5.1",
@@ -191,7 +187,6 @@
"webpack-dev-server": "4.15.1" "webpack-dev-server": "4.15.1"
}, },
"scripts": { "scripts": {
"postinstall": "patch-package",
"start": "webpack serve --mode development", "start": "webpack serve --mode development",
"dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build", "dev": "echo \"WARNING: npm run dev has been deprecated\" && npm run build",
"build:dataExplorer:ci": "npm run build:ci", "build:dataExplorer:ci": "npm run build:ci",
@@ -238,4 +233,4 @@
"printWidth": 120, "printWidth": 120,
"endOfLine": "auto" "endOfLine": "auto"
} }
} }

View File

@@ -1,22 +0,0 @@
diff --git a/node_modules/datatables.net-colreorder/types/types.d.ts b/node_modules/datatables.net-colreorder/types/types.d.ts
index e5dc283..1930c2b 100644
--- a/node_modules/datatables.net-colreorder/types/types.d.ts
+++ b/node_modules/datatables.net-colreorder/types/types.d.ts
@@ -7,7 +7,7 @@
/// <reference types="jquery" />
-import DataTables, {Api} from 'datatables.net';
+import DataTables, { Api } from 'datatables.net';
export default DataTables;
@@ -40,6 +40,8 @@ declare module 'datatables.net' {
/**
* Create a new ColReorder instance for the target DataTable
*/
+ // Ignore this error: error TS7013: Construct signature, which lacks return-type annotation, implicitly has an 'any' return type.
+ // @ts-ignore
new (dt: Api<any>, settings: boolean | ConfigColReorder);
/**

1954
src/Definitions/datatables.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@
* https://github.com/running-coder/jquery-typeahead/issues/156 * https://github.com/running-coder/jquery-typeahead/issues/156
* TODO: Replace this minimum definition by the official one when it comes out. * TODO: Replace this minimum definition by the official one when it comes out.
*/ */
/// <reference types="jquery" /> /// <reference path="jquery.d.ts" />
interface JQueryTypeaheadParam { interface JQueryTypeaheadParam {
input: string; input: string;

View File

@@ -3,7 +3,7 @@
// Definitions by: Boris Yankov <https://github.com/borisyankov/>, John Reilly <https://github.com/johnnyreilly> // Definitions by: Boris Yankov <https://github.com/borisyankov/>, John Reilly <https://github.com/johnnyreilly>
// Definitions: https://github.com/borisyankov/DefinitelyTyped // Definitions: https://github.com/borisyankov/DefinitelyTyped
/// <reference types="jquery"/> /// <reference path="jquery.d.ts"/>
declare namespace JQueryUI { declare namespace JQueryUI {
// Accordion ////////////////////////////////////////////////// // Accordion //////////////////////////////////////////////////

1890
src/Definitions/jquery.d.ts vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,12 +23,12 @@ describe("ThroughputInput Pane", () => {
}); });
it("should switch mode properly", () => { it("should switch mode properly", () => {
wrapper.find('[id="Manual-input"]').simulate("change"); wrapper.find('[aria-label="Manual database throughput"]').simulate("change");
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe( expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe(
"Container throughput (400 - unlimited RU/s)", "Container throughput (400 - unlimited RU/s)",
); );
wrapper.find('[id="Autoscale-input"]').simulate("change"); wrapper.find('[aria-label="Autoscale database throughput"]').simulate("change");
expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe("Container throughput (autoscale)"); expect(wrapper.find('[aria-label="Throughput header"]').at(0).text()).toBe("Container throughput (autoscale)");
}); });
}); });

View File

@@ -189,7 +189,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
<input <input
id="Autoscale-input" id="Autoscale-input"
className="throughputInputRadioBtn" className="throughputInputRadioBtn"
aria-label={`${getThroughputLabelText()} Autoscale`} aria-label="Autoscale database throughput"
aria-required={true} aria-required={true}
checked={isAutoscaleSelected} checked={isAutoscaleSelected}
type="radio" type="radio"
@@ -204,7 +204,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
<input <input
id="Manual-input" id="Manual-input"
className="throughputInputRadioBtn" className="throughputInputRadioBtn"
aria-label={`${getThroughputLabelText()} Manual`} aria-label="Manual database throughput"
checked={!isAutoscaleSelected} checked={!isAutoscaleSelected}
type="radio" type="radio"
aria-required={true} aria-required={true}
@@ -276,12 +276,6 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
</Link> </Link>
. .
</Text> </Text>
<Stack horizontal>
<Text variant="small" style={{ lineHeight: "20px", fontWeight: 600 }} aria-label="maxRUDescription">
{isDatabase ? "Database" : getCollectionName()} Required RU/s
</Text>
<InfoTooltip>{getAutoScaleTooltip()}</InfoTooltip>
</Stack>
<TooltipHost <TooltipHost
directionalHint={DirectionalHint.topLeftEdge} directionalHint={DirectionalHint.topLeftEdge}
@@ -302,7 +296,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
min={SharedConstants.CollectionCreation.DefaultCollectionRUs400} min={SharedConstants.CollectionCreation.DefaultCollectionRUs400}
max={userContext.isTryCosmosDBSubscription ? Constants.TryCosmosExperience.maxRU : Infinity} max={userContext.isTryCosmosDBSubscription ? Constants.TryCosmosExperience.maxRU : Infinity}
value={throughput.toString()} value={throughput.toString()}
ariaLabel={`${isDatabase ? "Database" : getCollectionName()} Required RU/s`} aria-label="Max request units per second"
required={true} required={true}
errorMessage={throughputError} errorMessage={throughputError}
/> />

View File

@@ -678,7 +678,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
role="radiogroup" role="radiogroup"
> >
<input <input
aria-label="Container throughput (autoscale) Autoscale" aria-label="Autoscale database throughput"
aria-required={true} aria-required={true}
checked={true} checked={true}
className="throughputInputRadioBtn" className="throughputInputRadioBtn"
@@ -695,7 +695,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
Autoscale Autoscale
</label> </label>
<input <input
aria-label="Container throughput (autoscale) Manual" aria-label="Manual database throughput"
aria-required={true} aria-required={true}
checked={false} checked={false}
className="throughputInputRadioBtn" className="throughputInputRadioBtn"

View File

@@ -3,10 +3,9 @@ import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
import { sendMessage } from "Common/MessageHandler"; import { sendMessage } from "Common/MessageHandler";
import { Platform, configContext } from "ConfigContext"; import { Platform, configContext } from "ConfigContext";
import { MessageTypes } from "Contracts/ExplorerContracts"; import { MessageTypes } from "Contracts/ExplorerContracts";
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient"; import { getCopilotEnabled } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { IGalleryItem } from "Juno/JunoClient"; import { IGalleryItem } from "Juno/JunoClient";
import { requestDatabaseResourceTokens } from "Platform/Fabric/FabricUtil"; import { requestDatabaseResourceTokens } from "Platform/Fabric/FabricUtil";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation"; import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointValidation";
import { useQueryCopilot } from "hooks/useQueryCopilot"; import { useQueryCopilot } from "hooks/useQueryCopilot";
import * as ko from "knockout"; import * as ko from "knockout";
@@ -1390,18 +1389,9 @@ export default class Explorer {
if (userContext.apiType !== "SQL" || !userContext.subscriptionId) { if (userContext.apiType !== "SQL" || !userContext.subscriptionId) {
return; return;
} }
const copilotEnabledPromise = getCopilotEnabled(); const copilotEnabled = await getCopilotEnabled();
const copilotUserDBEnabledPromise = isCopilotFeatureRegistered(userContext.subscriptionId); useQueryCopilot.getState().setCopilotEnabled(copilotEnabled);
const [copilotEnabled, copilotUserDBEnabled] = await Promise.all([ useQueryCopilot.getState().setCopilotUserDBEnabled(copilotEnabled);
copilotEnabledPromise,
copilotUserDBEnabledPromise,
]);
const copilotSampleDBEnabled = LocalStorageUtility.getEntryString(StorageKey.CopilotSampleDBEnabled) === "true";
useQueryCopilot.getState().setCopilotEnabled(copilotEnabled && copilotUserDBEnabled);
useQueryCopilot.getState().setCopilotUserDBEnabled(copilotUserDBEnabled);
useQueryCopilot
.getState()
.setCopilotSampleDBEnabled(copilotEnabled && copilotUserDBEnabled && copilotSampleDBEnabled);
} }
public async refreshSampleData(): Promise<void> { public async refreshSampleData(): Promise<void> {

View File

@@ -1163,12 +1163,15 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
)}"`, )}"`,
).then( ).then(
(documents: DataModels.DocumentId[]) => { (documents: DataModels.DocumentId[]) => {
$.each(documents, (index: number, doc: any) => { $.each(
newIconsMap[doc["_graph_icon_property_value"]] = { documents,
data: doc["icon"], (index: number, doc: { _graph_icon_property_value: string; icon: string; format: string }) => {
format: doc["format"], newIconsMap[doc["_graph_icon_property_value"]] = {
}; data: doc["icon"],
}); format: doc["format"],
};
},
);
// Update graph configuration // Update graph configuration
this.setState({ this.setState({

View File

@@ -200,7 +200,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
{ {
iconSrc: SettingsIcon, iconSrc: SettingsIcon,
iconAlt: "Settings", iconAlt: "Settings",
onCommandClick: () => useSidePanel.getState().openSidePanel("Settings", <SettingsPane explorer={container} />), onCommandClick: () => useSidePanel.getState().openSidePanel("Settings", <SettingsPane />),
commandButtonLabel: undefined, commandButtonLabel: undefined,
ariaLabel: "Settings", ariaLabel: "Settings",
tooltipText: "Settings", tooltipText: "Settings",

View File

@@ -154,7 +154,7 @@ function openPane(action: ActionContracts.OpenPane, explorer: Explorer) {
action.paneKind === ActionContracts.PaneKind.GlobalSettings || action.paneKind === ActionContracts.PaneKind.GlobalSettings ||
action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.GlobalSettings] action.paneKind === ActionContracts.PaneKind[ActionContracts.PaneKind.GlobalSettings]
) { ) {
useSidePanel.getState().openSidePanel("Settings", <SettingsPane explorer={explorer} />); useSidePanel.getState().openSidePanel("Settings", <SettingsPane />);
} }
} }

View File

@@ -29,7 +29,7 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
}; };
} }
componentDidMount(): void { omponentDidMount(): void {
window.addEventListener("resize", () => this.setState({ height: this.getPanelHeight() })); window.addEventListener("resize", () => this.setState({ height: this.getPanelHeight() }));
} }
@@ -62,12 +62,12 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
customWidth={this.props.panelWidth ? this.props.panelWidth : "440px"} customWidth={this.props.panelWidth ? this.props.panelWidth : "440px"}
headerClassName="panelHeader" headerClassName="panelHeader"
onRenderNavigationContent={this.props.onRenderNavigationContent} onRenderNavigationContent={this.props.onRenderNavigationContent}
isFooterAtBottom={true}
styles={{ styles={{
navigation: { borderBottom: "1px solid #cccccc" }, navigation: { borderBottom: "1px solid #cccccc" },
content: { padding: 0 }, content: { padding: 0, height: "100%" },
scrollableContent: { height: "100%" },
header: { padding: "0 0 8px 34px" }, header: { padding: "0 0 8px 34px" },
commands: { marginTop: 8, paddingTop: 0 }, commands: { marginTop: 8 },
}} }}
style={{ height: this.state.height }} style={{ height: this.state.height }}
> >
@@ -76,7 +76,44 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
); );
} }
public isFocusable(element: HTMLElement) {
return (
element.tabIndex >= 0 ||
(element instanceof HTMLAnchorElement && element.href) ||
(element instanceof HTMLAreaElement && element.href) ||
(element instanceof HTMLInputElement && !element.disabled) ||
(element instanceof HTMLSelectElement && !element.disabled) ||
(element instanceof HTMLTextAreaElement && !element.disabled) ||
(element instanceof HTMLButtonElement && !element.disabled)
);
}
private findFocusableParent = (element: HTMLElement) => {
while (element) {
if (this.isFocusable(element)) {
return element;
}
element = element.parentNode as HTMLElement;
}
return null;
};
private onDissmiss = (ev?: KeyboardEvent | React.SyntheticEvent<HTMLElement>): void => { private onDissmiss = (ev?: KeyboardEvent | React.SyntheticEvent<HTMLElement>): void => {
const elementIdToFocus = sessionStorage.getItem("focusedElementId") || null;
if (elementIdToFocus) {
const targetElement = document.getElementById(elementIdToFocus);
if (targetElement) {
const focusableParent = this.findFocusableParent(targetElement);
if (focusableParent) {
setTimeout(() => {
if (focusableParent) {
focusableParent.focus();
}
}, 100);
sessionStorage.removeItem("focusedElementId");
}
}
}
if (ev && (ev.target as HTMLElement).id === "notificationConsoleHeader") { if (ev && (ev.target as HTMLElement).id === "notificationConsoleHeader") {
ev.preventDefault(); ev.preventDefault();
} else { } else {

View File

@@ -6,7 +6,7 @@ import { SettingsPane } from "./SettingsPane";
describe("Settings Pane", () => { describe("Settings Pane", () => {
it("should render Default properly", () => { it("should render Default properly", () => {
const wrapper = shallow(<SettingsPane explorer={null} />); const wrapper = shallow(<SettingsPane />);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
@@ -18,7 +18,7 @@ describe("Settings Pane", () => {
}, },
} as DatabaseAccount, } as DatabaseAccount,
}); });
const wrapper = shallow(<SettingsPane explorer={null} />); const wrapper = shallow(<SettingsPane />);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
}); });

View File

@@ -16,20 +16,13 @@ import * as StringUtility from "Shared/StringUtility";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { logConsoleInfo } from "Utils/NotificationConsoleUtils"; import { logConsoleInfo } from "Utils/NotificationConsoleUtils";
import * as PriorityBasedExecutionUtils from "Utils/PriorityBasedExecutionUtils"; import * as PriorityBasedExecutionUtils from "Utils/PriorityBasedExecutionUtils";
import { useQueryCopilot } from "hooks/useQueryCopilot";
import { useSidePanel } from "hooks/useSidePanel"; import { useSidePanel } from "hooks/useSidePanel";
import React, { FunctionComponent, useState } from "react"; import React, { FunctionComponent, useState } from "react";
import Explorer from "../../Explorer";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm"; import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ export const SettingsPane: FunctionComponent = () => {
explorer,
}: {
explorer: Explorer;
}): JSX.Element => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
const [isExecuting, setIsExecuting] = useState<boolean>(false); const [isExecuting, setIsExecuting] = useState<boolean>(false);
const [refreshExplorer, setRefreshExplorer] = useState<boolean>(false);
const [pageOption, setPageOption] = useState<string>( const [pageOption, setPageOption] = useState<string>(
LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage) === Constants.Queries.unlimitedItemsPerPage LocalStorageUtility.getEntryNumber(StorageKey.ActualItemPerPage) === Constants.Queries.unlimitedItemsPerPage
? Constants.Queries.UnlimitedPageOption ? Constants.Queries.UnlimitedPageOption
@@ -85,17 +78,13 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
? LocalStorageUtility.getEntryString(StorageKey.PriorityLevel) ? LocalStorageUtility.getEntryString(StorageKey.PriorityLevel)
: Constants.PriorityLevel.Default, : Constants.PriorityLevel.Default,
); );
const [copilotSampleDBEnabled, setCopilotSampleDBEnabled] = useState<boolean>(
LocalStorageUtility.getEntryString(StorageKey.CopilotSampleDBEnabled) === "true",
);
const explorerVersion = configContext.gitSha; const explorerVersion = configContext.gitSha;
const shouldShowQueryPageOptions = userContext.apiType === "SQL"; const shouldShowQueryPageOptions = userContext.apiType === "SQL";
const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin"; const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin";
const shouldShowCrossPartitionOption = userContext.apiType !== "Gremlin"; const shouldShowCrossPartitionOption = userContext.apiType !== "Gremlin";
const shouldShowParallelismOption = userContext.apiType !== "Gremlin"; const shouldShowParallelismOption = userContext.apiType !== "Gremlin";
const shouldShowPriorityLevelOption = PriorityBasedExecutionUtils.isFeatureEnabled(); const shouldShowPriorityLevelOption = PriorityBasedExecutionUtils.isFeatureEnabled();
const shouldShowCopilotSampleDBOption = userContext.apiType === "SQL" && useQueryCopilot.getState().copilotEnabled; const handlerOnSubmit = () => {
const handlerOnSubmit = async () => {
setIsExecuting(true); setIsExecuting(true);
LocalStorageUtility.setEntryNumber( LocalStorageUtility.setEntryNumber(
@@ -111,7 +100,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, crossPartitionQueryEnabled.toString()); LocalStorageUtility.setEntryString(StorageKey.IsCrossPartitionQueryEnabled, crossPartitionQueryEnabled.toString());
LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism); LocalStorageUtility.setEntryNumber(StorageKey.MaxDegreeOfParellism, maxDegreeOfParallelism);
LocalStorageUtility.setEntryString(StorageKey.PriorityLevel, priorityLevel.toString()); LocalStorageUtility.setEntryString(StorageKey.PriorityLevel, priorityLevel.toString());
LocalStorageUtility.setEntryString(StorageKey.CopilotSampleDBEnabled, copilotSampleDBEnabled.toString());
if (shouldShowGraphAutoVizOption) { if (shouldShowGraphAutoVizOption) {
LocalStorageUtility.setEntryBoolean( LocalStorageUtility.setEntryBoolean(
@@ -151,7 +139,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
logConsoleInfo( logConsoleInfo(
`Updated query setting to ${LocalStorageUtility.getEntryString(StorageKey.SetPartitionKeyUndefined)}`, `Updated query setting to ${LocalStorageUtility.getEntryString(StorageKey.SetPartitionKeyUndefined)}`,
); );
refreshExplorer && (await explorer.refreshExplorer());
closeSidePanel(); closeSidePanel();
}; };
@@ -231,12 +218,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
} }
}; };
const handleSampleDatabaseChange = async (ev: React.MouseEvent<HTMLElement>, checked?: boolean): Promise<void> => {
setCopilotSampleDBEnabled(checked);
useQueryCopilot.getState().setCopilotSampleDBEnabled(checked);
setRefreshExplorer(false);
};
const choiceButtonStyles = { const choiceButtonStyles = {
root: { root: {
clear: "both", clear: "both",
@@ -453,7 +434,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</div> </div>
</div> </div>
<div className="settingsSection"> <div className="settingsSection">
<div className="settingsSectionPart settingsSectionInlineCheckbox"> <div className="settingsSectionPart">
<div className="settingsSectionLabel"> <div className="settingsSectionLabel">
Enable container pagination Enable container pagination
<InfoTooltip> <InfoTooltip>
@@ -473,7 +454,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</div> </div>
{shouldShowCrossPartitionOption && ( {shouldShowCrossPartitionOption && (
<div className="settingsSection"> <div className="settingsSection">
<div className="settingsSectionPart settingsSectionInlineCheckbox"> <div className="settingsSectionPart">
<div className="settingsSectionLabel"> <div className="settingsSectionLabel">
Enable cross-partition query Enable cross-partition query
<InfoTooltip> <InfoTooltip>
@@ -564,30 +545,6 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
</div> </div>
</div> </div>
)} )}
{shouldShowCopilotSampleDBOption && (
<div className="settingsSection">
<div className="settingsSectionPart settingsSectionInlineCheckbox">
<div className="settingsSectionLabel">
Enable sample database
<InfoTooltip>
This is a sample database and collection with synthetic product data you can use to explore using
NoSQL queries and Copilot. This will appear as another database in the Data Explorer UI, and is
created by, and maintained by Microsoft at no cost to you.
</InfoTooltip>
</div>
<Checkbox
styles={{
label: { padding: 0 },
}}
className="padding"
ariaLabel="Enable sample db for Copilot"
checked={copilotSampleDBEnabled}
onChange={handleSampleDatabaseChange}
/>
</div>
</div>
)}
<div className="settingsSection"> <div className="settingsSection">
<div className="settingsSectionPart"> <div className="settingsSectionPart">
<div className="settingsSectionLabel">Explorer Version</div> <div className="settingsSectionLabel">Explorer Version</div>

View File

@@ -274,7 +274,7 @@ exports[`Settings Pane should render Default properly 1`] = `
className="settingsSection" className="settingsSection"
> >
<div <div
className="settingsSectionPart settingsSectionInlineCheckbox" className="settingsSectionPart"
> >
<div <div
className="settingsSectionLabel" className="settingsSectionLabel"
@@ -303,7 +303,7 @@ exports[`Settings Pane should render Default properly 1`] = `
className="settingsSection" className="settingsSection"
> >
<div <div
className="settingsSectionPart settingsSectionInlineCheckbox" className="settingsSectionPart"
> >
<div <div
className="settingsSectionLabel" className="settingsSectionLabel"
@@ -521,7 +521,7 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
className="settingsSection" className="settingsSection"
> >
<div <div
className="settingsSectionPart settingsSectionInlineCheckbox" className="settingsSectionPart"
> >
<div <div
className="settingsSectionLabel" className="settingsSectionLabel"

View File

@@ -6,7 +6,6 @@ exports[`PaneContainerComponent test should be resize if notification console is
customWidth="440px" customWidth="440px"
headerClassName="panelHeader" headerClassName="panelHeader"
headerText="test" headerText="test"
isFooterAtBottom={true}
isLightDismiss={true} isLightDismiss={true}
isOpen={true} isOpen={true}
onDismiss={[Function]} onDismiss={[Function]}
@@ -19,9 +18,9 @@ exports[`PaneContainerComponent test should be resize if notification console is
Object { Object {
"commands": Object { "commands": Object {
"marginTop": 8, "marginTop": 8,
"paddingTop": 0,
}, },
"content": Object { "content": Object {
"height": "100%",
"padding": 0, "padding": 0,
}, },
"header": Object { "header": Object {
@@ -30,6 +29,9 @@ exports[`PaneContainerComponent test should be resize if notification console is
"navigation": Object { "navigation": Object {
"borderBottom": "1px solid #cccccc", "borderBottom": "1px solid #cccccc",
}, },
"scrollableContent": Object {
"height": "100%",
},
} }
} }
type={7} type={7}
@@ -46,7 +48,6 @@ exports[`PaneContainerComponent test should render with panel content and header
customWidth="440px" customWidth="440px"
headerClassName="panelHeader" headerClassName="panelHeader"
headerText="test" headerText="test"
isFooterAtBottom={true}
isLightDismiss={true} isLightDismiss={true}
isOpen={true} isOpen={true}
onDismiss={[Function]} onDismiss={[Function]}
@@ -59,9 +60,9 @@ exports[`PaneContainerComponent test should render with panel content and header
Object { Object {
"commands": Object { "commands": Object {
"marginTop": 8, "marginTop": 8,
"paddingTop": 0,
}, },
"content": Object { "content": Object {
"height": "100%",
"padding": 0, "padding": 0,
}, },
"header": Object { "header": Object {
@@ -70,6 +71,9 @@ exports[`PaneContainerComponent test should render with panel content and header
"navigation": Object { "navigation": Object {
"borderBottom": "1px solid #cccccc", "borderBottom": "1px solid #cccccc",
}, },
"scrollableContent": Object {
"height": "100%",
},
} }
} }
type={7} type={7}

View File

@@ -15,7 +15,6 @@ export const CopyPopup = ({
return showCopyPopup ? ( return showCopyPopup ? (
<Stack <Stack
role="status"
style={{ style={{
position: "fixed", position: "fixed",
width: 345, width: 345,

View File

@@ -4,7 +4,6 @@ exports[`Copy Popup snapshot test should render when showCopyPopup is false 1`]
exports[`Copy Popup snapshot test should render when showCopyPopup is true 1`] = ` exports[`Copy Popup snapshot test should render when showCopyPopup is true 1`] = `
<Stack <Stack
role="status"
style={ style={
Object { Object {
"background": "#FFFFFF", "background": "#FFFFFF",

View File

@@ -30,7 +30,6 @@ const CopilotProvider = ({ children }: { children: React.ReactNode }): JSX.Eleme
queryResults: undefined, queryResults: undefined,
errorMessage: "", errorMessage: "",
isSamplePromptsOpen: false, isSamplePromptsOpen: false,
showPromptTeachingBubble: true,
showDeletePopup: false, showDeletePopup: false,
showFeedbackBar: false, showFeedbackBar: false,
showCopyPopup: false, showCopyPopup: false,
@@ -66,7 +65,6 @@ const CopilotProvider = ({ children }: { children: React.ReactNode }): JSX.Eleme
setQueryResults: (queryResults: QueryResults | undefined) => set({ queryResults }), setQueryResults: (queryResults: QueryResults | undefined) => set({ queryResults }),
setErrorMessage: (errorMessage: string) => set({ errorMessage }), setErrorMessage: (errorMessage: string) => set({ errorMessage }),
setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => set({ isSamplePromptsOpen }), setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => set({ isSamplePromptsOpen }),
setShowPromptTeachingBubble: (showPromptTeachingBubble: boolean) => set({ showPromptTeachingBubble }),
setShowDeletePopup: (showDeletePopup: boolean) => set({ showDeletePopup }), setShowDeletePopup: (showDeletePopup: boolean) => set({ showDeletePopup }),
setShowFeedbackBar: (showFeedbackBar: boolean) => set({ showFeedbackBar }), setShowFeedbackBar: (showFeedbackBar: boolean) => set({ showFeedbackBar }),
setshowCopyPopup: (showCopyPopup: boolean) => set({ showCopyPopup }), setshowCopyPopup: (showCopyPopup: boolean) => set({ showCopyPopup }),
@@ -105,7 +103,6 @@ const CopilotProvider = ({ children }: { children: React.ReactNode }): JSX.Eleme
queryResults: undefined, queryResults: undefined,
errorMessage: "", errorMessage: "",
isSamplePromptsOpen: false, isSamplePromptsOpen: false,
showPromptTeachingBubble: true,
showDeletePopup: false, showDeletePopup: false,
showFeedbackBar: false, showFeedbackBar: false,
showCopyPopup: false, showCopyPopup: false,

View File

@@ -18,6 +18,7 @@ import {
Text, Text,
TextField, TextField,
} from "@fluentui/react"; } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks";
import { HttpStatusCodes } from "Common/Constants"; import { HttpStatusCodes } from "Common/Constants";
import { handleError } from "Common/ErrorHandlingUtils"; import { handleError } from "Common/ErrorHandlingUtils";
import { createUri } from "Common/UrlUtility"; import { createUri } from "Common/UrlUtility";
@@ -70,7 +71,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
databaseId, databaseId,
containerId, containerId,
}: QueryCopilotPromptProps): JSX.Element => { }: QueryCopilotPromptProps): JSX.Element => {
const [copilotTeachingBubbleVisible, setCopilotTeachingBubbleVisible] = useState<boolean>(false); const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
const inputEdited = useRef(false); const inputEdited = useRef(false);
const { const {
openFeedbackModal, openFeedbackModal,
@@ -93,8 +94,6 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
setIsSamplePromptsOpen, setIsSamplePromptsOpen,
showSamplePrompts, showSamplePrompts,
setShowSamplePrompts, setShowSamplePrompts,
showPromptTeachingBubble,
setShowPromptTeachingBubble,
showDeletePopup, showDeletePopup,
setShowDeletePopup, setShowDeletePopup,
showFeedbackBar, showFeedbackBar,
@@ -273,23 +272,16 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
}; };
const showTeachingBubble = (): void => { const showTeachingBubble = (): void => {
if (showPromptTeachingBubble && !inputEdited.current) { if (!inputEdited.current) {
setTimeout(() => { setTimeout(() => {
if (!inputEdited.current && !isWelcomModalVisible()) { if (!inputEdited.current && !isWelcomModalVisible()) {
setCopilotTeachingBubbleVisible(true); toggleCopilotTeachingBubbleVisible();
inputEdited.current = true; inputEdited.current = true;
} }
}, 30000); }, 30000);
} else {
toggleCopilotTeachingBubbleVisible(false);
} }
}; };
const toggleCopilotTeachingBubbleVisible = (visible: boolean): void => {
setCopilotTeachingBubbleVisible(visible);
setShowPromptTeachingBubble(visible);
};
const isWelcomModalVisible = (): boolean => { const isWelcomModalVisible = (): boolean => {
return localStorage.getItem("hideWelcomeModal") !== "true"; return localStorage.getItem("hideWelcomeModal") !== "true";
}; };
@@ -311,29 +303,15 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
resetButtonState(); resetButtonState();
}; };
const getAriaLabel = () => {
if (isGeneratingQuery === null) {
return " ";
} else if (isGeneratingQuery) {
return "Content is loading";
} else {
return "Content is updated";
}
};
React.useEffect(() => { React.useEffect(() => {
showTeachingBubble(); showTeachingBubble();
useTabs.getState().setIsQueryErrorThrown(false); useTabs.getState().setIsQueryErrorThrown(false);
}, []); }, []);
return ( return (
<Stack <Stack className="copilot-prompt-pane" styles={{ root: { backgroundColor: "#FAFAFA", padding: "16px 24px 0px" } }}>
className="copilot-prompt-pane"
styles={{ root: { backgroundColor: "#FAFAFA", padding: "16px 24px 0px" } }}
id="copilot-textfield-label"
>
<Stack horizontal> <Stack horizontal>
<Image src={CopilotIcon} style={{ width: 24, height: 24 }} alt="Copilot" role="none" /> <Image src={CopilotIcon} style={{ width: 24, height: 24 }} />
<Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text> <Text style={{ marginLeft: 8, fontWeight: 600, fontSize: 16 }}>Copilot</Text>
<IconButton <IconButton
iconProps={{ imageProps: { src: errorIcon } }} iconProps={{ imageProps: { src: errorIcon } }}
@@ -370,15 +348,14 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
disabled={isGeneratingQuery} disabled={isGeneratingQuery}
autoComplete="off" autoComplete="off"
placeholder="Ask a question in natural language and well generate the query for you." placeholder="Ask a question in natural language and well generate the query for you."
aria-labelledby="copilot-textfield-label"
/> />
{showPromptTeachingBubble && copilotTeachingBubbleVisible && ( {copilotTeachingBubbleVisible && (
<TeachingBubble <TeachingBubble
calloutProps={{ directionalHint: DirectionalHint.bottomCenter }} calloutProps={{ directionalHint: DirectionalHint.bottomCenter }}
target="#naturalLanguageInput" target="#naturalLanguageInput"
hasCloseButton={true} hasCloseButton={true}
closeButtonAriaLabel="Close" closeButtonAriaLabel="Close"
onDismiss={() => toggleCopilotTeachingBubbleVisible(false)} onDismiss={toggleCopilotTeachingBubbleVisible}
hasSmallHeadline={true} hasSmallHeadline={true}
headline="Write a prompt" headline="Write a prompt"
> >
@@ -386,7 +363,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
<Link <Link
onClick={() => { onClick={() => {
setShowSamplePrompts(true); setShowSamplePrompts(true);
toggleCopilotTeachingBubbleVisible(false); toggleCopilotTeachingBubbleVisible();
}} }}
style={{ color: "white", fontWeight: 600 }} style={{ color: "white", fontWeight: 600 }}
> >
@@ -400,11 +377,8 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
disabled={isGeneratingQuery || !userPrompt.trim()} disabled={isGeneratingQuery || !userPrompt.trim()}
style={{ marginLeft: 8 }} style={{ marginLeft: 8 }}
onClick={() => startGenerateQueryProcess()} onClick={() => startGenerateQueryProcess()}
aria-label="Send"
/> />
<div role="alert" aria-label={getAriaLabel()}> {isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
</div>
{showSamplePrompts && ( {showSamplePrompts && (
<Callout <Callout
styles={{ root: { minWidth: 400, maxWidth: "70vw" } }} styles={{ root: { minWidth: 400, maxWidth: "70vw" } }}
@@ -510,7 +484,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
<Stack style={{ margin: "8px 0" }}> <Stack style={{ margin: "8px 0" }}>
<Text style={{ fontSize: 12 }}> <Text style={{ fontSize: 12 }}>
AI-generated content can have mistakes. Make sure it&apos;s accurate and appropriate before using it.{" "} AI-generated content can have mistakes. Make sure it&apos;s accurate and appropriate before using it.{" "}
<Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank" style={{ color: "#0072c9" }}> <Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank">
Read preview terms Read preview terms
</Link> </Link>
{showErrorMessageBar && ( {showErrorMessageBar && (
@@ -578,7 +552,6 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
id="likeBtn" id="likeBtn"
style={{ marginLeft: 20 }} style={{ marginLeft: 20 }}
iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }} iconProps={{ iconName: likeQuery === true ? "LikeSolid" : "Like" }}
aria-label="Like"
onClick={() => { onClick={() => {
setShowCallout(!likeQuery); setShowCallout(!likeQuery);
setLikeQuery(!likeQuery); setLikeQuery(!likeQuery);
@@ -598,7 +571,6 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
setDislikeQuery(!dislikeQuery); setDislikeQuery(!dislikeQuery);
setShowCallout(false); setShowCallout(false);
}} }}
aria-label="Dislike"
/> />
<Separator vertical style={{ color: "#EDEBE9" }} /> <Separator vertical style={{ color: "#EDEBE9" }} />
<CommandBarButton <CommandBarButton

View File

@@ -15,12 +15,7 @@ import { MinimalQueryIterator } from "Common/IteratorUtilities";
import { createUri } from "Common/UrlUtility"; import { createUri } from "Common/UrlUtility";
import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage"; import { queryDocumentsPage } from "Common/dataAccess/queryDocumentsPage";
import { configContext } from "ConfigContext"; import { configContext } from "ConfigContext";
import { import { ContainerConnectionInfo, CopilotEnabledConfiguration, IProvisionData } from "Contracts/DataModels";
ContainerConnectionInfo,
CopilotEnabledConfiguration,
FeatureRegistration,
IProvisionData,
} from "Contracts/DataModels";
import { AuthorizationTokenHeaderMetadata, QueryResults } from "Contracts/ViewModels"; import { AuthorizationTokenHeaderMetadata, QueryResults } from "Contracts/ViewModels";
import { useDialog } from "Explorer/Controls/Dialog"; import { useDialog } from "Explorer/Controls/Dialog";
import Explorer from "Explorer/Explorer"; import Explorer from "Explorer/Explorer";
@@ -57,28 +52,6 @@ async function fetchWithTimeout(
return response; return response;
} }
export const isCopilotFeatureRegistered = async (subscriptionId: string): Promise<boolean> => {
const api_version = "2021-07-01";
const url = `${configContext.ARM_ENDPOINT}/subscriptions/${subscriptionId}/providers/Microsoft.Features/featureProviders/Microsoft.DocumentDB/subscriptionFeatureRegistrations/MicrosoftCopilotForAzureInCDB?api-version=${api_version}`;
const authorizationHeader: AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
const headers = { [authorizationHeader.header]: authorizationHeader.token };
let response;
try {
response = await fetchWithTimeout(url, headers);
} catch (error) {
return false;
}
if (!response?.ok) {
return false;
}
const featureRegistration = (await response?.json()) as FeatureRegistration;
return featureRegistration?.properties?.state === "Registered";
};
export const getCopilotEnabled = async (): Promise<boolean> => { export const getCopilotEnabled = async (): Promise<boolean> => {
const url = `${configContext.BACKEND_ENDPOINT}/api/portalsettings/querycopilot`; const url = `${configContext.BACKEND_ENDPOINT}/api/portalsettings/querycopilot`;
const authorizationHeader: AuthorizationTokenHeaderMetadata = getAuthorizationHeader(); const authorizationHeader: AuthorizationTokenHeaderMetadata = getAuthorizationHeader();

View File

@@ -49,7 +49,7 @@ exports[`Footer snapshot test should not pass if no text 1`] = `
/> />
</Stack> </Stack>
<StyledTextFieldBase <StyledTextFieldBase
disabled={null} disabled={false}
multiline={true} multiline={true}
onChange={[Function]} onChange={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
@@ -74,7 +74,7 @@ exports[`Footer snapshot test should not pass if no text 1`] = `
value="" value=""
/> />
<CustomizedIconButton <CustomizedIconButton
disabled={null} disabled={false}
iconProps={ iconProps={
Object { Object {
"iconName": "Send", "iconName": "Send",
@@ -134,7 +134,7 @@ exports[`Footer snapshot test should not pass text with non enter key 1`] = `
/> />
</Stack> </Stack>
<StyledTextFieldBase <StyledTextFieldBase
disabled={null} disabled={false}
multiline={true} multiline={true}
onChange={[Function]} onChange={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
@@ -159,7 +159,7 @@ exports[`Footer snapshot test should not pass text with non enter key 1`] = `
value="" value=""
/> />
<CustomizedIconButton <CustomizedIconButton
disabled={null} disabled={false}
iconProps={ iconProps={
Object { Object {
"iconName": "Send", "iconName": "Send",
@@ -219,7 +219,7 @@ exports[`Footer snapshot test should open sample prompts on button click 1`] = `
/> />
</Stack> </Stack>
<StyledTextFieldBase <StyledTextFieldBase
disabled={null} disabled={false}
multiline={true} multiline={true}
onChange={[Function]} onChange={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
@@ -244,7 +244,7 @@ exports[`Footer snapshot test should open sample prompts on button click 1`] = `
value="" value=""
/> />
<CustomizedIconButton <CustomizedIconButton
disabled={null} disabled={false}
iconProps={ iconProps={
Object { Object {
"iconName": "Send", "iconName": "Send",
@@ -304,7 +304,7 @@ exports[`Footer snapshot test should pass text with enter key 1`] = `
/> />
</Stack> </Stack>
<StyledTextFieldBase <StyledTextFieldBase
disabled={null} disabled={false}
multiline={true} multiline={true}
onChange={[Function]} onChange={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
@@ -329,7 +329,7 @@ exports[`Footer snapshot test should pass text with enter key 1`] = `
value="test message" value="test message"
/> />
<CustomizedIconButton <CustomizedIconButton
disabled={null} disabled={false}
iconProps={ iconProps={
Object { Object {
"iconName": "Send", "iconName": "Send",
@@ -389,7 +389,7 @@ exports[`Footer snapshot test should pass text with icon button 1`] = `
/> />
</Stack> </Stack>
<StyledTextFieldBase <StyledTextFieldBase
disabled={null} disabled={false}
multiline={true} multiline={true}
onChange={[Function]} onChange={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
@@ -414,7 +414,7 @@ exports[`Footer snapshot test should pass text with icon button 1`] = `
value="test message" value="test message"
/> />
<CustomizedIconButton <CustomizedIconButton
disabled={null} disabled={false}
iconProps={ iconProps={
Object { Object {
"iconName": "Send", "iconName": "Send",
@@ -474,7 +474,7 @@ exports[`Footer snapshot test should update user input 1`] = `
/> />
</Stack> </Stack>
<StyledTextFieldBase <StyledTextFieldBase
disabled={null} disabled={false}
multiline={true} multiline={true}
onChange={[Function]} onChange={[Function]}
onKeyDown={[Function]} onKeyDown={[Function]}
@@ -499,7 +499,7 @@ exports[`Footer snapshot test should update user input 1`] = `
value="" value=""
/> />
<CustomizedIconButton <CustomizedIconButton
disabled={null} disabled={false}
iconProps={ iconProps={
Object { Object {
"iconName": "Send", "iconName": "Send",

View File

@@ -148,25 +148,23 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
/> />
</Stack> </Stack>
<Stack horizontal tokens={{ childrenGap: 16 }}> <Stack horizontal tokens={{ childrenGap: 16 }}>
{useQueryCopilot.getState().copilotEnabled && ( <SplashScreenButton
<SplashScreenButton imgSrc={CopilotIcon}
imgSrc={CopilotIcon} title={"Query faster with Copilot"}
title={"Query faster with Copilot"} description={
description={ "Copilot is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
"Copilot is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!" }
onClick={() => {
const copilotVersion = userContext.features.copilotVersion;
if (copilotVersion === "v1.0") {
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
} else if (copilotVersion === "v2.0") {
const sampleCollection = useDatabases.getState().sampleDataResourceTokenCollection;
sampleCollection.onNewQueryClick(sampleCollection, undefined);
} }
onClick={() => { traceOpen(Action.OpenQueryCopilotFromSplashScreen, { apiType: userContext.apiType });
const copilotVersion = userContext.features.copilotVersion; }}
if (copilotVersion === "v1.0") { />
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
} else if (copilotVersion === "v2.0") {
const sampleCollection = useDatabases.getState().sampleDataResourceTokenCollection;
sampleCollection.onNewQueryClick(sampleCollection, undefined);
}
traceOpen(Action.OpenQueryCopilotFromSplashScreen, { apiType: userContext.apiType });
}}
/>
)}
<SplashScreenButton <SplashScreenButton
imgSrc={ConnectIcon} imgSrc={ConnectIcon}
title={"Connect"} title={"Connect"}

View File

@@ -1,8 +1,6 @@
import * as ko from "knockout"; import * as ko from "knockout";
import * as _ from "underscore"; import * as _ from "underscore";
import * as DataTable from "datatables.net-dt";
import loadingIndicator3Squares from "../../../../images/LoadingIndicator_3Squares.gif";
import QueryTablesTab from "../../Tabs/QueryTablesTab"; import QueryTablesTab from "../../Tabs/QueryTablesTab";
import * as Constants from "../Constants"; import * as Constants from "../Constants";
import * as Entities from "../Entities"; import * as Entities from "../Entities";
@@ -96,7 +94,7 @@ function createDataTable(
}); });
} }
tableEntityListViewModel.table = DataTableBuilder.createDataTable($dataTable, <DataTable.Config>{ tableEntityListViewModel.table = DataTableBuilder.createDataTable($dataTable, <DataTables.Settings>{
// WARNING!!! SECURITY: If you add new columns, make sure you encode them if they are user strings from Azure (see encodeText) // WARNING!!! SECURITY: If you add new columns, make sure you encode them if they are user strings from Azure (see encodeText)
// so that they don't get interpreted as HTML in our page. // so that they don't get interpreted as HTML in our page.
colReorder: true, colReorder: true,
@@ -118,7 +116,7 @@ function createDataTable(
sPrevious: "<", sPrevious: "<",
sLast: ">>", sLast: ">>",
}, },
sProcessing: `<img style="width: 28px; height: 6px; " src="${loadingIndicator3Squares}">`, sProcessing: '<img style="width: 28px; height: 6px; " src="images/LoadingIndicator_3Squares.gif">',
oAria: { oAria: {
sSortAscending: "", sSortAscending: "",
sSortDescending: "", sSortDescending: "",
@@ -347,7 +345,7 @@ function updateSelectionStatus(oSettings: any): void {
// TODO consider centralizing this "post-command" logic into some sort of Command Manager entity. // TODO consider centralizing this "post-command" logic into some sort of Command Manager entity.
// See VSO:166520: "[Storage Explorer] Consider adding a 'command manager' to track command post-effects." // See VSO:166520: "[Storage Explorer] Consider adding a 'command manager' to track command post-effects."
function updateDataTableFocus(queryTablesTabId: string): void { function updateDataTableFocus(queryTablesTabId: string): void {
var $activeElement: JQuery<Element> = $(document.activeElement); var $activeElement: JQuery = $(document.activeElement);
var isFocusLost: boolean = $activeElement.is("body"); // When focus is lost, "body" becomes the active element. var isFocusLost: boolean = $activeElement.is("body"); // When focus is lost, "body" becomes the active element.
var storageExplorerFrameHasFocus: boolean = document.hasFocus(); var storageExplorerFrameHasFocus: boolean = document.hasFocus();
var operationManager = tableEntityListViewModelMap[queryTablesTabId].operationManager; var operationManager = tableEntityListViewModelMap[queryTablesTabId].operationManager;

View File

@@ -1,4 +1,3 @@
import * as DataTable from "datatables.net-dt";
import * as Utilities from "../Utilities"; import * as Utilities from "../Utilities";
/** /**
@@ -9,7 +8,7 @@ import * as Utilities from "../Utilities";
* @param{$dataTableElem} JQuery data table element * @param{$dataTableElem} JQuery data table element
* @param{$settings} Settings to use when creating the data table * @param{$settings} Settings to use when creating the data table
*/ */
export function createDataTable($dataTableElem: JQuery, settings: any): DataTable.Api<HTMLElement> { export function createDataTable($dataTableElem: JQuery, settings: any): DataTables.DataTable {
return $dataTableElem.DataTable(applyDefaultRendering(settings)); return $dataTableElem.DataTable(applyDefaultRendering(settings));
} }
@@ -19,14 +18,14 @@ export function createDataTable($dataTableElem: JQuery, settings: any): DataTabl
* @param{settings} The settings to check * @param{settings} The settings to check
* @return The given settings with all columns having a rendering function * @return The given settings with all columns having a rendering function
*/ */
function applyDefaultRendering(settings: DataTable.Config): any { function applyDefaultRendering(settings: any): DataTables.SettingsLegacy {
var tableColumns: any[] = null; var tableColumns: DataTables.ColumnLegacy[] = null;
if (settings.columns) { if (settings.aoColumns) {
tableColumns = settings.columns; tableColumns = settings.aoColumns;
} else if (settings.columnDefs) { } else if (settings.aoColumnDefs) {
// for tables we use aoColumnDefs instead of aoColumns // for tables we use aoColumnDefs instead of aoColumns
tableColumns = settings.columnDefs; tableColumns = settings.aoColumnDefs;
} }
// either the settings had no columns defined, or they were called // either the settings had no columns defined, or they were called

View File

@@ -1,11 +1,11 @@
import ko from "knockout"; import ko from "knockout";
import * as Constants from "../Constants";
import * as Entities from "../Entities";
import * as Utilities from "../Utilities";
import * as DataTableOperations from "./DataTableOperations"; import * as DataTableOperations from "./DataTableOperations";
import * as Constants from "../Constants";
import TableCommands from "./TableCommands"; import TableCommands from "./TableCommands";
import TableEntityListViewModel from "./TableEntityListViewModel"; import TableEntityListViewModel from "./TableEntityListViewModel";
import * as Utilities from "../Utilities";
import * as Entities from "../Entities";
/* /*
* Base class for data table row selection. * Base class for data table row selection.
@@ -13,9 +13,9 @@ import TableEntityListViewModel from "./TableEntityListViewModel";
export default class DataTableOperationManager { export default class DataTableOperationManager {
private _tableEntityListViewModel: TableEntityListViewModel; private _tableEntityListViewModel: TableEntityListViewModel;
private _tableCommands: TableCommands; private _tableCommands: TableCommands;
private dataTable: JQuery<Element>; private dataTable: JQuery;
constructor(table: JQuery<Element>, viewModel: TableEntityListViewModel, tableCommands: TableCommands) { constructor(table: JQuery, viewModel: TableEntityListViewModel, tableCommands: TableCommands) {
this.dataTable = table; this.dataTable = table;
this._tableEntityListViewModel = viewModel; this._tableEntityListViewModel = viewModel;
this._tableCommands = tableCommands; this._tableCommands = tableCommands;
@@ -25,7 +25,7 @@ export default class DataTableOperationManager {
} }
private click = (event: JQueryEventObject) => { private click = (event: JQueryEventObject) => {
var elem: JQuery<Element> = $(event.currentTarget); var elem: JQuery = $(event.currentTarget);
this.updateLastSelectedItem(elem, event.shiftKey); this.updateLastSelectedItem(elem, event.shiftKey);
if (Utilities.isEnvironmentCtrlPressed(event)) { if (Utilities.isEnvironmentCtrlPressed(event)) {
@@ -48,7 +48,7 @@ export default class DataTableOperationManager {
if (isUpArrowKey || isDownArrowKey) { if (isUpArrowKey || isDownArrowKey) {
var lastSelectedItem: Entities.ITableEntity = this._tableEntityListViewModel.lastSelectedItem; var lastSelectedItem: Entities.ITableEntity = this._tableEntityListViewModel.lastSelectedItem;
var dataTableRows: JQuery<Element> = $(Constants.htmlSelectors.dataTableAllRowsSelector); var dataTableRows: JQuery = $(Constants.htmlSelectors.dataTableAllRowsSelector);
var maximumIndex = dataTableRows.length - 1; var maximumIndex = dataTableRows.length - 1;
// If can't find an index for lastSelectedItem, then either no item is previously selected or it goes across page. // If can't find an index for lastSelectedItem, then either no item is previously selected or it goes across page.
@@ -60,7 +60,7 @@ export default class DataTableOperationManager {
: -1; : -1;
var nextIndex: number = isUpArrowKey ? lastSelectedItemIndex - 1 : lastSelectedItemIndex + 1; var nextIndex: number = isUpArrowKey ? lastSelectedItemIndex - 1 : lastSelectedItemIndex + 1;
var safeIndex: number = Utilities.ensureBetweenBounds(nextIndex, 0, maximumIndex); var safeIndex: number = Utilities.ensureBetweenBounds(nextIndex, 0, maximumIndex);
var selectedRowElement: JQuery<Element> = dataTableRows.eq(safeIndex); var selectedRowElement: JQuery = dataTableRows.eq(safeIndex);
if (selectedRowElement) { if (selectedRowElement) {
if (event.shiftKey) { if (event.shiftKey) {
@@ -143,13 +143,13 @@ export default class DataTableOperationManager {
return handled; return handled;
} }
private getEntityIdentity($elem: JQuery<Element>): Entities.ITableEntityIdentity { private getEntityIdentity($elem: JQuery): Entities.ITableEntityIdentity {
return { return {
RowKey: $elem.attr(Constants.htmlAttributeNames.dataTableRowKeyAttr), RowKey: $elem.attr(Constants.htmlAttributeNames.dataTableRowKeyAttr),
}; };
} }
private updateLastSelectedItem($elem: JQuery<Element>, isShiftSelect: boolean) { private updateLastSelectedItem($elem: JQuery, isShiftSelect: boolean) {
var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem); var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem);
var entity = this._tableEntityListViewModel.getItemFromCurrentPage( var entity = this._tableEntityListViewModel.getItemFromCurrentPage(
this._tableEntityListViewModel.getTableEntityKeys(entityIdentity.RowKey), this._tableEntityListViewModel.getTableEntityKeys(entityIdentity.RowKey),
@@ -162,7 +162,7 @@ export default class DataTableOperationManager {
} }
} }
private applySingleSelection($elem: JQuery<Element>) { private applySingleSelection($elem: JQuery) {
if ($elem) { if ($elem) {
var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem); var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem);
@@ -179,7 +179,7 @@ export default class DataTableOperationManager {
); );
} }
private applyCtrlSelection($elem: JQuery<Element>): void { private applyCtrlSelection($elem: JQuery): void {
var koSelected: ko.ObservableArray<Entities.ITableEntity> = this._tableEntityListViewModel var koSelected: ko.ObservableArray<Entities.ITableEntity> = this._tableEntityListViewModel
? this._tableEntityListViewModel.selected ? this._tableEntityListViewModel.selected
: null; : null;
@@ -200,7 +200,7 @@ export default class DataTableOperationManager {
} }
} }
private applyShiftSelection($elem: JQuery<Element>): void { private applyShiftSelection($elem: JQuery): void {
var anchorItem = this._tableEntityListViewModel.lastSelectedAnchorItem; var anchorItem = this._tableEntityListViewModel.lastSelectedAnchorItem;
// If anchor item doesn't exist, use the first available item of current page instead // If anchor item doesn't exist, use the first available item of current page instead
@@ -228,7 +228,7 @@ export default class DataTableOperationManager {
} }
} }
private applyContextMenuSelection($elem: JQuery<Element>) { private applyContextMenuSelection($elem: JQuery) {
var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem); var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem);
if ( if (

View File

@@ -1,4 +1,3 @@
import * as DataTables from "datatables.net";
import Q from "q"; import Q from "q";
import _ from "underscore"; import _ from "underscore";
import * as QueryBuilderConstants from "../Constants"; import * as QueryBuilderConstants from "../Constants";
@@ -14,7 +13,7 @@ export function getRowSelector(selectorSchema: Entities.IProperty[]): string {
return QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector + selector; return QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector + selector;
} }
export function isRowVisible(dataTableScrollBodyQuery: JQuery<Element>, element: Element): boolean { export function isRowVisible(dataTableScrollBodyQuery: JQuery, element: HTMLElement): boolean {
let isVisible = false; let isVisible = false;
if (dataTableScrollBodyQuery.length && element) { if (dataTableScrollBodyQuery.length && element) {
@@ -27,18 +26,16 @@ export function isRowVisible(dataTableScrollBodyQuery: JQuery<Element>, element:
return isVisible; return isVisible;
} }
export function scrollToRowIfNeeded(dataTableRows: JQuery<Element>, currentIndex: number, isScrollUp: boolean): void { export function scrollToRowIfNeeded(dataTableRows: JQuery, currentIndex: number, isScrollUp: boolean): void {
if (dataTableRows.length) { if (dataTableRows.length) {
const dataTableScrollBodyQuery: JQuery<Element> = $( const dataTableScrollBodyQuery: JQuery = $(QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector),
QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector, selectedRowElement: HTMLElement = dataTableRows.get(currentIndex);
),
selectedRowElement: Element = dataTableRows.get(currentIndex);
if (dataTableScrollBodyQuery.length && selectedRowElement) { if (dataTableScrollBodyQuery.length && selectedRowElement) {
const isVisible: boolean = isRowVisible(dataTableScrollBodyQuery, selectedRowElement); const isVisible: boolean = isRowVisible(dataTableScrollBodyQuery, selectedRowElement);
if (!isVisible) { if (!isVisible) {
const selectedRowQuery: JQuery<Element> = $(selectedRowElement), const selectedRowQuery: JQuery = $(selectedRowElement),
scrollPosition: number = dataTableScrollBodyQuery.scrollTop(), scrollPosition: number = dataTableScrollBodyQuery.scrollTop(),
selectedElementPosition: number = selectedRowQuery.position().top; selectedElementPosition: number = selectedRowQuery.position().top;
let newScrollPosition = 0; let newScrollPosition = 0;
@@ -57,8 +54,8 @@ export function scrollToRowIfNeeded(dataTableRows: JQuery<Element>, currentIndex
} }
export function scrollToTopIfNeeded(): void { export function scrollToTopIfNeeded(): void {
const $dataTableRows: JQuery<Element> = $(QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector), const $dataTableRows: JQuery = $(QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector),
$dataTableScrollBody: JQuery<Element> = $(QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector); $dataTableScrollBody: JQuery = $(QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector);
if ($dataTableRows.length && $dataTableScrollBody.length) { if ($dataTableRows.length && $dataTableScrollBody.length) {
$dataTableScrollBody.scrollTop(0); $dataTableScrollBody.scrollTop(0);
@@ -74,7 +71,7 @@ export function setPaginationButtonEventHandlers(): void {
.attr("role", "button"); .attr("role", "button");
} }
export function filterColumns(table: DataTables.Api<HTMLElement>, settings: boolean[]): void { export function filterColumns(table: DataTables.DataTable, settings: boolean[]): void {
settings && settings &&
settings.forEach((value: boolean, index: number) => { settings.forEach((value: boolean, index: number) => {
table.column(index).visible(value, false); table.column(index).visible(value, false);
@@ -87,7 +84,7 @@ export function filterColumns(table: DataTables.Api<HTMLElement>, settings: bool
* If no current order is specified, reorder the columns based on intial order. * If no current order is specified, reorder the columns based on intial order.
*/ */
export function reorderColumns( export function reorderColumns(
table: DataTables.Api<HTMLElement>, table: DataTables.DataTable,
targetOrder: number[], targetOrder: number[],
currentOrder?: number[], currentOrder?: number[],
//eslint-disable-next-line //eslint-disable-next-line
@@ -111,9 +108,7 @@ export function reorderColumns(
? calculateTransformationOrder(currentOrder, targetOrder) ? calculateTransformationOrder(currentOrder, targetOrder)
: targetOrder; : targetOrder;
try { try {
// TODO: This possibly does not work with the new version of datatables. $.fn.dataTable.ColReorder(table).fnOrder(transformationOrder);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
($.fn.dataTable as any).ColReorder(table).fnOrder(transformationOrder);
} catch (err) { } catch (err) {
return Q.reject(err); return Q.reject(err);
} }
@@ -121,9 +116,9 @@ export function reorderColumns(
return Q.resolve(null); return Q.resolve(null);
} }
// export function resetColumns(table: DataTables.DataTable): void { export function resetColumns(table: DataTables.DataTable): void {
// $.fn.dataTable.ColReorder(table).fnReset(); $.fn.dataTable.ColReorder(table).fnReset();
// } }
/** /**
* A table's initial order is described in the form of a natural ascending order. * A table's initial order is described in the form of a natural ascending order.
@@ -138,10 +133,8 @@ export function getInitialOrder(columnsCount: number): number[] {
* Initial order: I = [0, 1, 2, 3, 4, 5, 6, 7, 8] <----> {prop0, prop1, prop2, prop3, prop4, prop5, prop6, prop7, prop8} * Initial order: I = [0, 1, 2, 3, 4, 5, 6, 7, 8] <----> {prop0, prop1, prop2, prop3, prop4, prop5, prop6, prop7, prop8}
* Current order: C = [0, 1, 2, 6, 7, 3, 4, 5, 8] <----> {prop0, prop1, prop2, prop6, prop7, prop3, prop4, prop5, prop8} * Current order: C = [0, 1, 2, 6, 7, 3, 4, 5, 8] <----> {prop0, prop1, prop2, prop6, prop7, prop3, prop4, prop5, prop8}
*/ */
export function getCurrentOrder(table: DataTables.Api<HTMLElement>): number[] { export function getCurrentOrder(table: DataTables.DataTable): number[] {
// TODO: This possibly does not work with the new version of datatables. return $.fn.dataTable.ColReorder(table).fnOrder();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return ($.fn.dataTable as any).ColReorder(table).fnOrder();
} }
/** /**
@@ -185,8 +178,8 @@ export function calculateTransformationOrder(currentOrder: number[], targetOrder
return transformationOrder; return transformationOrder;
} }
export function getDataTableHeaders(table: DataTables.Api<HTMLElement>): string[] { export function getDataTableHeaders(table: DataTables.DataTable): string[] {
const columns = table.columns(); const columns: DataTables.ColumnsMethods = table.columns();
let headers: string[] = []; let headers: string[] = [];
if (columns) { if (columns) {
// table.columns() return ColumnsMethods which is an array of arrays // table.columns() return ColumnsMethods which is an array of arrays

View File

@@ -1,15 +1,14 @@
import * as ko from "knockout"; import * as ko from "knockout";
import * as _ from "underscore"; import * as _ from "underscore";
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import * as DataTables from "datatables.net";
import * as CommonConstants from "../../../Common/Constants";
import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import CacheBase from "./CacheBase";
import QueryTablesTab from "../../Tabs/QueryTablesTab"; import * as CommonConstants from "../../../Common/Constants";
import * as Constants from "../Constants"; import * as Constants from "../Constants";
import * as Entities from "../Entities"; import * as Entities from "../Entities";
import CacheBase from "./CacheBase"; import QueryTablesTab from "../../Tabs/QueryTablesTab";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos";
// This is the format of the data we will have to pass to Datatable render callback, // This is the format of the data we will have to pass to Datatable render callback,
// and property names are defined by Datatable as well. // and property names are defined by Datatable as well.
@@ -28,7 +27,7 @@ abstract class DataTableViewModel {
public items = ko.observableArray<Entities.ITableEntity>(); public items = ko.observableArray<Entities.ITableEntity>();
public selected = ko.observableArray<Entities.ITableEntity>(); public selected = ko.observableArray<Entities.ITableEntity>();
public table: DataTables.Api<HTMLElement>; public table: DataTables.DataTable;
// The anchor item is for shift selection. i.e., select all items between anchor item and a give item. // The anchor item is for shift selection. i.e., select all items between anchor item and a give item.
public lastSelectedAnchorItem: Entities.ITableEntity; public lastSelectedAnchorItem: Entities.ITableEntity;

View File

@@ -1,4 +1,3 @@
import * as DataTables from "datatables.net";
import * as ko from "knockout"; import * as ko from "knockout";
import Q from "q"; import Q from "q";
import * as _ from "underscore"; import * as _ from "underscore";
@@ -57,7 +56,7 @@ function _parse(err: any): ErrorDataModel[] {
function _getInnerErrors(message: string): any[] { function _getInnerErrors(message: string): any[] {
/* /*
The backend error message has an inner-message which is a stringified object. The backend error message has an inner-message which is a stringified object.
For SQL errors, the "errors" property is an array of SqlErrorDataModel. For SQL errors, the "errors" property is an array of SqlErrorDataModel.
Example: Example:
"Message: {"Errors":["Resource with specified id or name already exists"]}\r\nActivityId: 80005000008d40b6a, Request URI: /apps/19000c000c0a0005/services/mctestdocdbprod-MasterService-0-00066ab9937/partitions/900005f9000e676fb8/replicas/13000000000955p" "Message: {"Errors":["Resource with specified id or name already exists"]}\r\nActivityId: 80005000008d40b6a, Request URI: /apps/19000c000c0a0005/services/mctestdocdbprod-MasterService-0-00066ab9937/partitions/900005f9000e676fb8/replicas/13000000000955p"
@@ -132,7 +131,7 @@ export default class TableEntityListViewModel extends DataTableViewModel {
return [{ key: Constants.EntityKeyNames.RowKey, value: rowKey }]; return [{ key: Constants.EntityKeyNames.RowKey, value: rowKey }];
} }
public reloadTable(useSetting: boolean = true, resetHeaders: boolean = true): DataTables.Api<Element> { public reloadTable(useSetting: boolean = true, resetHeaders: boolean = true): DataTables.DataTable {
this.clearCache(); this.clearCache();
this.clearSelection(); this.clearSelection();
this.isCancelled = false; this.isCancelled = false;

View File

@@ -1,4 +1,3 @@
import * as DataTable from "datatables.net-dt";
import * as ko from "knockout"; import * as ko from "knockout";
import { KeyCodes } from "../../../Common/Constants"; import { KeyCodes } from "../../../Common/Constants";
import { userContext } from "../../../UserContext"; import { userContext } from "../../../UserContext";
@@ -644,7 +643,7 @@ export default class QueryBuilderViewModel {
return groupViewModels; return groupViewModels;
}; };
public runQuery = (): DataTable.Api<Element> => { public runQuery = (): DataTables.DataTable => {
return this._queryViewModel.runQuery(); return this._queryViewModel.runQuery();
}; };

View File

@@ -1,10 +1,9 @@
import * as DataTables from "datatables.net";
import * as ko from "knockout"; import * as ko from "knockout";
import React from "react"; import React from "react";
import * as _ from "underscore"; import * as _ from "underscore";
import { KeyCodes } from "../../../Common/Constants"; import { KeyCodes } from "../../../Common/Constants";
import { userContext } from "../../../UserContext";
import { useSidePanel } from "../../../hooks/useSidePanel"; import { useSidePanel } from "../../../hooks/useSidePanel";
import { userContext } from "../../../UserContext";
import { TableQuerySelectPanel } from "../../Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel"; import { TableQuerySelectPanel } from "../../Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel";
import QueryTablesTab from "../../Tabs/QueryTablesTab"; import QueryTablesTab from "../../Tabs/QueryTablesTab";
import { getQuotedCqlIdentifier } from "../CqlUtilities"; import { getQuotedCqlIdentifier } from "../CqlUtilities";
@@ -159,7 +158,7 @@ export default class QueryViewModel {
notify: "always", notify: "always",
}); });
public runQuery = (): DataTables.Api<Element> => { public runQuery = (): DataTables.DataTable => {
let filter = this.setFilter(); let filter = this.setFilter();
if (filter && userContext.apiType !== "Cassandra") { if (filter && userContext.apiType !== "Cassandra") {
filter = filter.replace(/"/g, "'"); filter = filter.replace(/"/g, "'");
@@ -177,7 +176,7 @@ export default class QueryViewModel {
return this._tableEntityListViewModel.reloadTable(/*useSetting*/ false, /*resetHeaders*/ false); return this._tableEntityListViewModel.reloadTable(/*useSetting*/ false, /*resetHeaders*/ false);
}; };
public clearQuery = (): DataTables.Api<Element> => { public clearQuery = (): DataTables.DataTable => {
this.queryText(); this.queryText();
this.topValue(); this.topValue();
this.selectText(); this.selectText();

View File

@@ -281,7 +281,7 @@ export class CassandraAPIDataClient extends TableDataClient {
query, query,
paginationToken, paginationToken,
}, },
beforeSend: this.setAuthorizationHeader as any, beforeSend: this.setAuthorizationHeader,
cache: false, cache: false,
}); });
shouldNotify && shouldNotify &&
@@ -423,7 +423,7 @@ export class CassandraAPIDataClient extends TableDataClient {
keyspaceId: collection.databaseId, keyspaceId: collection.databaseId,
tableId: collection.id(), tableId: collection.id(),
}, },
beforeSend: this.setAuthorizationHeader as any, beforeSend: this.setAuthorizationHeader,
cache: false, cache: false,
}) })
.then( .then(
@@ -463,7 +463,7 @@ export class CassandraAPIDataClient extends TableDataClient {
keyspaceId: collection.databaseId, keyspaceId: collection.databaseId,
tableId: collection.id(), tableId: collection.id(),
}, },
beforeSend: this.setAuthorizationHeader as any, beforeSend: this.setAuthorizationHeader,
cache: false, cache: false,
}) })
.then( .then(
@@ -496,7 +496,7 @@ export class CassandraAPIDataClient extends TableDataClient {
resourceId: resourceId, resourceId: resourceId,
query: query, query: query,
}, },
beforeSend: this.setAuthorizationHeader as any, beforeSend: this.setAuthorizationHeader,
cache: false, cache: false,
}).then( }).then(
(data: any) => { (data: any) => {

View File

@@ -16,7 +16,7 @@ import React from "react";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
export const PostgresConnectTab: React.FC = (): JSX.Element => { export const PostgresConnectTab: React.FC = (): JSX.Element => {
const { adminLogin, databaseName, nodes, enablePublicIpAccess } = userContext.postgresConnectionStrParams; const { adminLogin, nodes, enablePublicIpAccess } = userContext.postgresConnectionStrParams;
const [usePgBouncerPort, setUsePgBouncerPort] = React.useState<boolean>(false); const [usePgBouncerPort, setUsePgBouncerPort] = React.useState<boolean>(false);
const [selectedNode, setSelectedNode] = React.useState<string>(nodes?.[0]?.value); const [selectedNode, setSelectedNode] = React.useState<string>(nodes?.[0]?.value);
const portNumber = usePgBouncerPort ? "6432" : "5432"; const portNumber = usePgBouncerPort ? "6432" : "5432";
@@ -40,11 +40,11 @@ export const PostgresConnectTab: React.FC = (): JSX.Element => {
text: node.text, text: node.text,
})); }));
const postgresSQLConnectionURL = `postgres://${adminLogin}:{your_password}@${selectedNode}:${portNumber}/${databaseName}?sslmode=require`; const postgresSQLConnectionURL = `postgres://${adminLogin}:{your_password}@${selectedNode}:${portNumber}/citus?sslmode=require`;
const psql = `psql "host=${selectedNode} port=${portNumber} dbname=${databaseName} user=${adminLogin} password={your_password} sslmode=require"`; const psql = `psql "host=${selectedNode} port=${portNumber} dbname=citus user=${adminLogin} password={your_password} sslmode=require"`;
const jdbc = `jdbc:postgresql://${selectedNode}:${portNumber}/${databaseName}?user=${adminLogin}&password={your_password}&sslmode=require`; const jdbc = `jdbc:postgresql://${selectedNode}:${portNumber}/citus?user=${adminLogin}&password={your_password}&sslmode=require`;
const libpq = `host=${selectedNode} port=${portNumber} dbname=${databaseName} user=${adminLogin} password={your_password} sslmode=require`; const libpq = `host=${selectedNode} port=${portNumber} dbname=citus user=${adminLogin} password={your_password} sslmode=require`;
const adoDotNet = `Server=${selectedNode};Database=${databaseName};Port=${portNumber};User Id=${adminLogin};Password={your_password};Ssl Mode=Require;`; const adoDotNet = `Server=${selectedNode};Database=citus;Port=${portNumber};User Id=${adminLogin};Password={your_password};Ssl Mode=Require;`;
return ( return (
<div style={{ width: "100%", padding: 16 }}> <div style={{ width: "100%", padding: 16 }}>

View File

@@ -224,6 +224,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
}; };
public onSaveQueryClick = (): void => { public onSaveQueryClick = (): void => {
sessionStorage.setItem("focusedElementId", "savequery");
useSidePanel.getState().openSidePanel("Save Query", <SaveQueryPane explorer={this.props.collection.container} />); useSidePanel.getState().openSidePanel("Save Query", <SaveQueryPane explorer={this.props.collection.container} />);
}; };
@@ -394,6 +395,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
const label = "Save Query"; const label = "Save Query";
buttons.push({ buttons.push({
iconSrc: SaveQueryIcon, iconSrc: SaveQueryIcon,
id: "savequery",
iconAlt: label, iconAlt: label,
onCommandClick: this.onSaveQueryClick, onCommandClick: this.onSaveQueryClick,
commandButtonLabel: label, commandButtonLabel: label,
@@ -444,7 +446,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
this._toggleCopilot(!this.state.copilotActive); this._toggleCopilot(!this.state.copilotActive);
}, },
commandButtonLabel: this.state.copilotActive ? "Disable Copilot" : "Enable Copilot", commandButtonLabel: this.state.copilotActive ? "Disable Copilot" : "Enable Copilot",
ariaLabel: this.state.copilotActive ? "Disable Copilot" : "Enable Copilot", ariaLabel: "Copilot",
hasPopup: false, hasPopup: false,
}; };
buttons.push(toggleCopilotButton); buttons.push(toggleCopilotButton);

View File

@@ -769,10 +769,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
const dataRootNode = buildDataTree(); const dataRootNode = buildDataTree();
const isSampleDataEnabled = const isSampleDataEnabled =
useQueryCopilot().copilotEnabled && useQueryCopilot().copilotEnabled && userContext.sampleDataConnectionInfo && userContext.apiType === "SQL";
useQueryCopilot().copilotSampleDBEnabled &&
userContext.sampleDataConnectionInfo &&
userContext.apiType === "SQL";
const sampleDataResourceTokenCollection = useDatabases((state) => state.sampleDataResourceTokenCollection); const sampleDataResourceTokenCollection = useDatabases((state) => state.sampleDataResourceTokenCollection);
return ( return (

View File

@@ -11,7 +11,6 @@ export enum StorageKey {
MaxWaitTimeInSeconds, MaxWaitTimeInSeconds,
AutomaticallyCancelQueryAfterTimeout, AutomaticallyCancelQueryAfterTimeout,
ContainerPaginationEnabled, ContainerPaginationEnabled,
CopilotSampleDBEnabled,
CustomItemPerPage, CustomItemPerPage,
DatabaseAccountId, DatabaseAccountId,
EncryptedKeyToken, EncryptedKeyToken,

View File

@@ -36,7 +36,6 @@ export interface Node {
export interface PostgresConnectionStrParams { export interface PostgresConnectionStrParams {
adminLogin: string; adminLogin: string;
databaseName: string;
enablePublicIpAccess: boolean; enablePublicIpAccess: boolean;
nodes: Node[]; nodes: Node[];
isMarlinServerGroup: boolean; isMarlinServerGroup: boolean;

View File

@@ -10,7 +10,6 @@ import { ContainerInfo } from "../Contracts/DataModels";
export interface QueryCopilotState { export interface QueryCopilotState {
copilotEnabled: boolean; copilotEnabled: boolean;
copilotUserDBEnabled: boolean; copilotUserDBEnabled: boolean;
copilotSampleDBEnabled: boolean;
generatedQuery: string; generatedQuery: string;
likeQuery: boolean; likeQuery: boolean;
userPrompt: string; userPrompt: string;
@@ -29,7 +28,6 @@ export interface QueryCopilotState {
queryResults: QueryResults | undefined; queryResults: QueryResults | undefined;
errorMessage: string; errorMessage: string;
isSamplePromptsOpen: boolean; isSamplePromptsOpen: boolean;
showPromptTeachingBubble: boolean;
showDeletePopup: boolean; showDeletePopup: boolean;
showFeedbackBar: boolean; showFeedbackBar: boolean;
showCopyPopup: boolean; showCopyPopup: boolean;
@@ -52,7 +50,6 @@ export interface QueryCopilotState {
setCopilotEnabled: (copilotEnabled: boolean) => void; setCopilotEnabled: (copilotEnabled: boolean) => void;
setCopilotUserDBEnabled: (copilotUserDBEnabled: boolean) => void; setCopilotUserDBEnabled: (copilotUserDBEnabled: boolean) => void;
setCopilotSampleDBEnabled: (copilotSampleDBEnabled: boolean) => void;
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void; openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => void;
closeFeedbackModal: () => void; closeFeedbackModal: () => void;
setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) => void; setHideFeedbackModalForLikedQueries: (hideFeedbackModalForLikedQueries: boolean) => void;
@@ -72,7 +69,6 @@ export interface QueryCopilotState {
setQueryResults: (queryResults: QueryResults | undefined) => void; setQueryResults: (queryResults: QueryResults | undefined) => void;
setErrorMessage: (errorMessage: string) => void; setErrorMessage: (errorMessage: string) => void;
setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => void; setIsSamplePromptsOpen: (isSamplePromptsOpen: boolean) => void;
setShowPromptTeachingBubble: (showPromptTeachingBubble: boolean) => void;
setShowDeletePopup: (showDeletePopup: boolean) => void; setShowDeletePopup: (showDeletePopup: boolean) => void;
setShowFeedbackBar: (showFeedbackBar: boolean) => void; setShowFeedbackBar: (showFeedbackBar: boolean) => void;
setshowCopyPopup: (showCopyPopup: boolean) => void; setshowCopyPopup: (showCopyPopup: boolean) => void;
@@ -95,12 +91,11 @@ export interface QueryCopilotState {
resetQueryCopilotStates: () => void; resetQueryCopilotStates: () => void;
} }
type QueryCopilotStore = UseStore<Partial<QueryCopilotState>>; type QueryCopilotStore = UseStore<QueryCopilotState>;
export const useQueryCopilot: QueryCopilotStore = create((set) => ({ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
copilotEnabled: false, copilotEnabled: false,
copilotUserDBEnabled: false, copilotUserDBEnabled: false,
copilotSampleDBEnabled: false,
generatedQuery: "", generatedQuery: "",
likeQuery: false, likeQuery: false,
userPrompt: "", userPrompt: "",
@@ -109,7 +104,7 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
correlationId: "", correlationId: "",
query: "SELECT * FROM c", query: "SELECT * FROM c",
selectedQuery: "", selectedQuery: "",
isGeneratingQuery: null, isGeneratingQuery: false,
isGeneratingExplanation: false, isGeneratingExplanation: false,
isExecuting: false, isExecuting: false,
dislikeQuery: undefined, dislikeQuery: undefined,
@@ -150,7 +145,6 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
setCopilotEnabled: (copilotEnabled: boolean) => set({ copilotEnabled }), setCopilotEnabled: (copilotEnabled: boolean) => set({ copilotEnabled }),
setCopilotUserDBEnabled: (copilotUserDBEnabled: boolean) => set({ copilotUserDBEnabled }), setCopilotUserDBEnabled: (copilotUserDBEnabled: boolean) => set({ copilotUserDBEnabled }),
setCopilotSampleDBEnabled: (copilotSampleDBEnabled: boolean) => set({ copilotSampleDBEnabled }),
openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) => openFeedbackModal: (generatedQuery: string, likeQuery: boolean, userPrompt: string) =>
set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }), set({ generatedQuery, likeQuery, userPrompt, showFeedbackModal: true }),
closeFeedbackModal: () => set({ showFeedbackModal: false }), closeFeedbackModal: () => set({ showFeedbackModal: false }),