mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-27 12:51:41 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7314f6d259 | ||
|
|
a616cf8bda | ||
|
|
5d13463bdb | ||
|
|
c4cceceafc | ||
|
|
532a453f5a | ||
|
|
9355a3ae04 | ||
|
|
14456c2102 | ||
|
|
0c9264e8b3 | ||
|
|
0dd1032357 | ||
|
|
5011d12f16 | ||
|
|
a7e5ff2a9f | ||
|
|
ad1391f623 | ||
|
|
a2a5407b15 | ||
|
|
e9181f19d7 |
@@ -2897,9 +2897,21 @@ 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 {
|
||||||
|
|||||||
1406
package-lock.json
generated
1406
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -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.5.1",
|
"datatables.net-colreorder-dt": "1.7.0",
|
||||||
"datatables.net-dt": "1.10.19",
|
"datatables.net-dt": "1.13.8",
|
||||||
"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,13 +71,14 @@
|
|||||||
"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.5.1",
|
"jquery": "3.7.1",
|
||||||
"jquery-typeahead": "2.10.6",
|
"jquery-typeahead": "2.11.1",
|
||||||
"jquery-ui-dist": "1.12.1",
|
"jquery-ui-dist": "1.13.2",
|
||||||
"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",
|
||||||
@@ -114,11 +115,14 @@
|
|||||||
"@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",
|
||||||
@@ -187,6 +191,7 @@
|
|||||||
"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",
|
||||||
|
|||||||
22
patches/datatables.net-colreorder+1.7.0.patch
Normal file
22
patches/datatables.net-colreorder+1.7.0.patch
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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
1954
src/Definitions/datatables.d.ts
vendored
File diff suppressed because it is too large
Load Diff
2
src/Definitions/jquery-typescript.d.ts
vendored
2
src/Definitions/jquery-typescript.d.ts
vendored
@@ -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 path="jquery.d.ts" />
|
/// <reference types="jquery" />
|
||||||
|
|
||||||
interface JQueryTypeaheadParam {
|
interface JQueryTypeaheadParam {
|
||||||
input: string;
|
input: string;
|
||||||
|
|||||||
2
src/Definitions/jquery-ui.d.ts
vendored
2
src/Definitions/jquery-ui.d.ts
vendored
@@ -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 path="jquery.d.ts"/>
|
/// <reference types="jquery"/>
|
||||||
|
|
||||||
declare namespace JQueryUI {
|
declare namespace JQueryUI {
|
||||||
// Accordion //////////////////////////////////////////////////
|
// Accordion //////////////////////////////////////////////////
|
||||||
|
|||||||
1890
src/Definitions/jquery.d.ts
vendored
1890
src/Definitions/jquery.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -23,12 +23,12 @@ describe("ThroughputInput Pane", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should switch mode properly", () => {
|
it("should switch mode properly", () => {
|
||||||
wrapper.find('[aria-label="Manual database throughput"]').simulate("change");
|
wrapper.find('[id="Manual-input"]').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('[aria-label="Autoscale database throughput"]').simulate("change");
|
wrapper.find('[id="Autoscale-input"]').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)");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
<input
|
<input
|
||||||
id="Autoscale-input"
|
id="Autoscale-input"
|
||||||
className="throughputInputRadioBtn"
|
className="throughputInputRadioBtn"
|
||||||
aria-label="Autoscale database throughput"
|
aria-label={`${getThroughputLabelText()} Autoscale`}
|
||||||
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="Manual database throughput"
|
aria-label={`${getThroughputLabelText()} Manual`}
|
||||||
checked={!isAutoscaleSelected}
|
checked={!isAutoscaleSelected}
|
||||||
type="radio"
|
type="radio"
|
||||||
aria-required={true}
|
aria-required={true}
|
||||||
@@ -276,6 +276,12 @@ 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}
|
||||||
@@ -296,7 +302,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()}
|
||||||
aria-label="Max request units per second"
|
ariaLabel={`${isDatabase ? "Database" : getCollectionName()} Required RU/s`}
|
||||||
required={true}
|
required={true}
|
||||||
errorMessage={throughputError}
|
errorMessage={throughputError}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -678,7 +678,7 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
role="radiogroup"
|
role="radiogroup"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-label="Autoscale database throughput"
|
aria-label="Container throughput (autoscale) Autoscale"
|
||||||
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="Manual database throughput"
|
aria-label="Container throughput (autoscale) Manual"
|
||||||
aria-required={true}
|
aria-required={true}
|
||||||
checked={false}
|
checked={false}
|
||||||
className="throughputInputRadioBtn"
|
className="throughputInputRadioBtn"
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ 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 } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
|
import { getCopilotEnabled, isCopilotFeatureRegistered } 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";
|
||||||
@@ -1389,9 +1390,18 @@ export default class Explorer {
|
|||||||
if (userContext.apiType !== "SQL" || !userContext.subscriptionId) {
|
if (userContext.apiType !== "SQL" || !userContext.subscriptionId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const copilotEnabled = await getCopilotEnabled();
|
const copilotEnabledPromise = getCopilotEnabled();
|
||||||
useQueryCopilot.getState().setCopilotEnabled(copilotEnabled);
|
const copilotUserDBEnabledPromise = isCopilotFeatureRegistered(userContext.subscriptionId);
|
||||||
useQueryCopilot.getState().setCopilotUserDBEnabled(copilotEnabled);
|
const [copilotEnabled, copilotUserDBEnabled] = await Promise.all([
|
||||||
|
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> {
|
||||||
|
|||||||
@@ -1163,15 +1163,12 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
|
|||||||
)}"`,
|
)}"`,
|
||||||
).then(
|
).then(
|
||||||
(documents: DataModels.DocumentId[]) => {
|
(documents: DataModels.DocumentId[]) => {
|
||||||
$.each(
|
$.each(documents, (index: number, doc: any) => {
|
||||||
documents,
|
newIconsMap[doc["_graph_icon_property_value"]] = {
|
||||||
(index: number, doc: { _graph_icon_property_value: string; icon: string; format: string }) => {
|
data: doc["icon"],
|
||||||
newIconsMap[doc["_graph_icon_property_value"]] = {
|
format: doc["format"],
|
||||||
data: doc["icon"],
|
};
|
||||||
format: doc["format"],
|
});
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update graph configuration
|
// Update graph configuration
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ export function createControlCommandBarButtons(container: Explorer): CommandButt
|
|||||||
{
|
{
|
||||||
iconSrc: SettingsIcon,
|
iconSrc: SettingsIcon,
|
||||||
iconAlt: "Settings",
|
iconAlt: "Settings",
|
||||||
onCommandClick: () => useSidePanel.getState().openSidePanel("Settings", <SettingsPane />),
|
onCommandClick: () => useSidePanel.getState().openSidePanel("Settings", <SettingsPane explorer={container} />),
|
||||||
commandButtonLabel: undefined,
|
commandButtonLabel: undefined,
|
||||||
ariaLabel: "Settings",
|
ariaLabel: "Settings",
|
||||||
tooltipText: "Settings",
|
tooltipText: "Settings",
|
||||||
|
|||||||
@@ -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 />);
|
useSidePanel.getState().openSidePanel("Settings", <SettingsPane explorer={explorer} />);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
omponentDidMount(): void {
|
componentDidMount(): 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, height: "100%" },
|
content: { padding: 0 },
|
||||||
scrollableContent: { height: "100%" },
|
|
||||||
header: { padding: "0 0 8px 34px" },
|
header: { padding: "0 0 8px 34px" },
|
||||||
commands: { marginTop: 8 },
|
commands: { marginTop: 8, paddingTop: 0 },
|
||||||
}}
|
}}
|
||||||
style={{ height: this.state.height }}
|
style={{ height: this.state.height }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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 />);
|
const wrapper = shallow(<SettingsPane explorer={null} />);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ describe("Settings Pane", () => {
|
|||||||
},
|
},
|
||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
const wrapper = shallow(<SettingsPane />);
|
const wrapper = shallow(<SettingsPane explorer={null} />);
|
||||||
expect(wrapper).toMatchSnapshot();
|
expect(wrapper).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,13 +16,20 @@ 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 = () => {
|
export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||||
|
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
|
||||||
@@ -78,13 +85,17 @@ export const SettingsPane: FunctionComponent = () => {
|
|||||||
? 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 handlerOnSubmit = () => {
|
const shouldShowCopilotSampleDBOption = userContext.apiType === "SQL" && useQueryCopilot.getState().copilotEnabled;
|
||||||
|
const handlerOnSubmit = async () => {
|
||||||
setIsExecuting(true);
|
setIsExecuting(true);
|
||||||
|
|
||||||
LocalStorageUtility.setEntryNumber(
|
LocalStorageUtility.setEntryNumber(
|
||||||
@@ -100,6 +111,7 @@ export const SettingsPane: FunctionComponent = () => {
|
|||||||
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(
|
||||||
@@ -139,6 +151,7 @@ export const SettingsPane: FunctionComponent = () => {
|
|||||||
logConsoleInfo(
|
logConsoleInfo(
|
||||||
`Updated query setting to ${LocalStorageUtility.getEntryString(StorageKey.SetPartitionKeyUndefined)}`,
|
`Updated query setting to ${LocalStorageUtility.getEntryString(StorageKey.SetPartitionKeyUndefined)}`,
|
||||||
);
|
);
|
||||||
|
refreshExplorer && (await explorer.refreshExplorer());
|
||||||
closeSidePanel();
|
closeSidePanel();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -218,6 +231,12 @@ export const SettingsPane: FunctionComponent = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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",
|
||||||
@@ -434,7 +453,7 @@ export const SettingsPane: FunctionComponent = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="settingsSection">
|
<div className="settingsSection">
|
||||||
<div className="settingsSectionPart">
|
<div className="settingsSectionPart settingsSectionInlineCheckbox">
|
||||||
<div className="settingsSectionLabel">
|
<div className="settingsSectionLabel">
|
||||||
Enable container pagination
|
Enable container pagination
|
||||||
<InfoTooltip>
|
<InfoTooltip>
|
||||||
@@ -454,7 +473,7 @@ export const SettingsPane: FunctionComponent = () => {
|
|||||||
</div>
|
</div>
|
||||||
{shouldShowCrossPartitionOption && (
|
{shouldShowCrossPartitionOption && (
|
||||||
<div className="settingsSection">
|
<div className="settingsSection">
|
||||||
<div className="settingsSectionPart">
|
<div className="settingsSectionPart settingsSectionInlineCheckbox">
|
||||||
<div className="settingsSectionLabel">
|
<div className="settingsSectionLabel">
|
||||||
Enable cross-partition query
|
Enable cross-partition query
|
||||||
<InfoTooltip>
|
<InfoTooltip>
|
||||||
@@ -545,6 +564,30 @@ export const SettingsPane: FunctionComponent = () => {
|
|||||||
</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>
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ exports[`Settings Pane should render Default properly 1`] = `
|
|||||||
className="settingsSection"
|
className="settingsSection"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="settingsSectionPart"
|
className="settingsSectionPart settingsSectionInlineCheckbox"
|
||||||
>
|
>
|
||||||
<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"
|
className="settingsSectionPart settingsSectionInlineCheckbox"
|
||||||
>
|
>
|
||||||
<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"
|
className="settingsSectionPart settingsSectionInlineCheckbox"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="settingsSectionLabel"
|
className="settingsSectionLabel"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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]}
|
||||||
@@ -18,9 +19,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 {
|
||||||
@@ -29,9 +30,6 @@ 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}
|
||||||
@@ -48,6 +46,7 @@ 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]}
|
||||||
@@ -60,9 +59,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 {
|
||||||
@@ -71,9 +70,6 @@ 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}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const CopyPopup = ({
|
|||||||
|
|
||||||
return showCopyPopup ? (
|
return showCopyPopup ? (
|
||||||
<Stack
|
<Stack
|
||||||
|
role="status"
|
||||||
style={{
|
style={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
width: 345,
|
width: 345,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ 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,
|
||||||
@@ -65,6 +66,7 @@ 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 }),
|
||||||
@@ -103,6 +105,7 @@ 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,
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ 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";
|
||||||
@@ -71,7 +70,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||||||
databaseId,
|
databaseId,
|
||||||
containerId,
|
containerId,
|
||||||
}: QueryCopilotPromptProps): JSX.Element => {
|
}: QueryCopilotPromptProps): JSX.Element => {
|
||||||
const [copilotTeachingBubbleVisible, { toggle: toggleCopilotTeachingBubbleVisible }] = useBoolean(false);
|
const [copilotTeachingBubbleVisible, setCopilotTeachingBubbleVisible] = useState<boolean>(false);
|
||||||
const inputEdited = useRef(false);
|
const inputEdited = useRef(false);
|
||||||
const {
|
const {
|
||||||
openFeedbackModal,
|
openFeedbackModal,
|
||||||
@@ -94,6 +93,8 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||||||
setIsSamplePromptsOpen,
|
setIsSamplePromptsOpen,
|
||||||
showSamplePrompts,
|
showSamplePrompts,
|
||||||
setShowSamplePrompts,
|
setShowSamplePrompts,
|
||||||
|
showPromptTeachingBubble,
|
||||||
|
setShowPromptTeachingBubble,
|
||||||
showDeletePopup,
|
showDeletePopup,
|
||||||
setShowDeletePopup,
|
setShowDeletePopup,
|
||||||
showFeedbackBar,
|
showFeedbackBar,
|
||||||
@@ -272,16 +273,23 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const showTeachingBubble = (): void => {
|
const showTeachingBubble = (): void => {
|
||||||
if (!inputEdited.current) {
|
if (showPromptTeachingBubble && !inputEdited.current) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!inputEdited.current && !isWelcomModalVisible()) {
|
if (!inputEdited.current && !isWelcomModalVisible()) {
|
||||||
toggleCopilotTeachingBubbleVisible();
|
setCopilotTeachingBubbleVisible(true);
|
||||||
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";
|
||||||
};
|
};
|
||||||
@@ -303,15 +311,29 @@ 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 className="copilot-prompt-pane" styles={{ root: { backgroundColor: "#FAFAFA", padding: "16px 24px 0px" } }}>
|
<Stack
|
||||||
|
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 }} />
|
<Image src={CopilotIcon} style={{ width: 24, height: 24 }} alt="Copilot" role="none" />
|
||||||
<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 } }}
|
||||||
@@ -348,14 +370,15 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||||||
disabled={isGeneratingQuery}
|
disabled={isGeneratingQuery}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
placeholder="Ask a question in natural language and we’ll generate the query for you."
|
placeholder="Ask a question in natural language and we’ll generate the query for you."
|
||||||
|
aria-labelledby="copilot-textfield-label"
|
||||||
/>
|
/>
|
||||||
{copilotTeachingBubbleVisible && (
|
{showPromptTeachingBubble && 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}
|
onDismiss={() => toggleCopilotTeachingBubbleVisible(false)}
|
||||||
hasSmallHeadline={true}
|
hasSmallHeadline={true}
|
||||||
headline="Write a prompt"
|
headline="Write a prompt"
|
||||||
>
|
>
|
||||||
@@ -363,7 +386,7 @@ export const QueryCopilotPromptbar: React.FC<QueryCopilotPromptProps> = ({
|
|||||||
<Link
|
<Link
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowSamplePrompts(true);
|
setShowSamplePrompts(true);
|
||||||
toggleCopilotTeachingBubbleVisible();
|
toggleCopilotTeachingBubbleVisible(false);
|
||||||
}}
|
}}
|
||||||
style={{ color: "white", fontWeight: 600 }}
|
style={{ color: "white", fontWeight: 600 }}
|
||||||
>
|
>
|
||||||
@@ -377,8 +400,11 @@ 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"
|
||||||
/>
|
/>
|
||||||
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
<div role="alert" aria-label={getAriaLabel()}>
|
||||||
|
{isGeneratingQuery && <Spinner style={{ marginLeft: 8 }} />}
|
||||||
|
</div>
|
||||||
{showSamplePrompts && (
|
{showSamplePrompts && (
|
||||||
<Callout
|
<Callout
|
||||||
styles={{ root: { minWidth: 400, maxWidth: "70vw" } }}
|
styles={{ root: { minWidth: 400, maxWidth: "70vw" } }}
|
||||||
@@ -484,7 +510,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's accurate and appropriate before using it.{" "}
|
AI-generated content can have mistakes. Make sure it's accurate and appropriate before using it.{" "}
|
||||||
<Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank">
|
<Link href="https://aka.ms/cdb-copilot-preview-terms" target="_blank" style={{ color: "#0072c9" }}>
|
||||||
Read preview terms
|
Read preview terms
|
||||||
</Link>
|
</Link>
|
||||||
{showErrorMessageBar && (
|
{showErrorMessageBar && (
|
||||||
@@ -552,6 +578,7 @@ 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);
|
||||||
@@ -571,6 +598,7 @@ 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
|
||||||
|
|||||||
@@ -15,7 +15,12 @@ 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 { ContainerConnectionInfo, CopilotEnabledConfiguration, IProvisionData } from "Contracts/DataModels";
|
import {
|
||||||
|
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";
|
||||||
@@ -52,6 +57,28 @@ 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();
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ exports[`Footer snapshot test should not pass if no text 1`] = `
|
|||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<StyledTextFieldBase
|
<StyledTextFieldBase
|
||||||
disabled={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
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={false}
|
disabled={null}
|
||||||
iconProps={
|
iconProps={
|
||||||
Object {
|
Object {
|
||||||
"iconName": "Send",
|
"iconName": "Send",
|
||||||
|
|||||||
@@ -148,23 +148,25 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack horizontal tokens={{ childrenGap: 16 }}>
|
<Stack horizontal tokens={{ childrenGap: 16 }}>
|
||||||
<SplashScreenButton
|
{useQueryCopilot.getState().copilotEnabled && (
|
||||||
imgSrc={CopilotIcon}
|
<SplashScreenButton
|
||||||
title={"Query faster with Copilot"}
|
imgSrc={CopilotIcon}
|
||||||
description={
|
title={"Query faster with Copilot"}
|
||||||
"Copilot is your AI buddy that helps you write Azure Cosmos DB queries like a pro. Try it using our sample data set now!"
|
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!"
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
traceOpen(Action.OpenQueryCopilotFromSplashScreen, { apiType: userContext.apiType });
|
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);
|
||||||
|
}
|
||||||
|
traceOpen(Action.OpenQueryCopilotFromSplashScreen, { apiType: userContext.apiType });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<SplashScreenButton
|
<SplashScreenButton
|
||||||
imgSrc={ConnectIcon}
|
imgSrc={ConnectIcon}
|
||||||
title={"Connect"}
|
title={"Connect"}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
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";
|
||||||
@@ -94,7 +96,7 @@ function createDataTable(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tableEntityListViewModel.table = DataTableBuilder.createDataTable($dataTable, <DataTables.Settings>{
|
tableEntityListViewModel.table = DataTableBuilder.createDataTable($dataTable, <DataTable.Config>{
|
||||||
// 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,
|
||||||
@@ -116,7 +118,7 @@ function createDataTable(
|
|||||||
sPrevious: "<",
|
sPrevious: "<",
|
||||||
sLast: ">>",
|
sLast: ">>",
|
||||||
},
|
},
|
||||||
sProcessing: '<img style="width: 28px; height: 6px; " src="images/LoadingIndicator_3Squares.gif">',
|
sProcessing: `<img style="width: 28px; height: 6px; " src="${loadingIndicator3Squares}">`,
|
||||||
oAria: {
|
oAria: {
|
||||||
sSortAscending: "",
|
sSortAscending: "",
|
||||||
sSortDescending: "",
|
sSortDescending: "",
|
||||||
@@ -345,7 +347,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 = $(document.activeElement);
|
var $activeElement: JQuery<Element> = $(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;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import * as DataTable from "datatables.net-dt";
|
||||||
import * as Utilities from "../Utilities";
|
import * as Utilities from "../Utilities";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,7 +9,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): DataTables.DataTable {
|
export function createDataTable($dataTableElem: JQuery, settings: any): DataTable.Api<HTMLElement> {
|
||||||
return $dataTableElem.DataTable(applyDefaultRendering(settings));
|
return $dataTableElem.DataTable(applyDefaultRendering(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,14 +19,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: any): DataTables.SettingsLegacy {
|
function applyDefaultRendering(settings: DataTable.Config): any {
|
||||||
var tableColumns: DataTables.ColumnLegacy[] = null;
|
var tableColumns: any[] = null;
|
||||||
|
|
||||||
if (settings.aoColumns) {
|
if (settings.columns) {
|
||||||
tableColumns = settings.aoColumns;
|
tableColumns = settings.columns;
|
||||||
} else if (settings.aoColumnDefs) {
|
} else if (settings.columnDefs) {
|
||||||
// for tables we use aoColumnDefs instead of aoColumns
|
// for tables we use aoColumnDefs instead of aoColumns
|
||||||
tableColumns = settings.aoColumnDefs;
|
tableColumns = settings.columnDefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// either the settings had no columns defined, or they were called
|
// either the settings had no columns defined, or they were called
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import ko from "knockout";
|
import ko from "knockout";
|
||||||
|
|
||||||
import * as DataTableOperations from "./DataTableOperations";
|
|
||||||
import * as Constants from "../Constants";
|
import * as Constants from "../Constants";
|
||||||
|
import * as Entities from "../Entities";
|
||||||
|
import * as Utilities from "../Utilities";
|
||||||
|
import * as DataTableOperations from "./DataTableOperations";
|
||||||
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 * as Entities from "../Entities";
|
|||||||
export default class DataTableOperationManager {
|
export default class DataTableOperationManager {
|
||||||
private _tableEntityListViewModel: TableEntityListViewModel;
|
private _tableEntityListViewModel: TableEntityListViewModel;
|
||||||
private _tableCommands: TableCommands;
|
private _tableCommands: TableCommands;
|
||||||
private dataTable: JQuery;
|
private dataTable: JQuery<Element>;
|
||||||
|
|
||||||
constructor(table: JQuery, viewModel: TableEntityListViewModel, tableCommands: TableCommands) {
|
constructor(table: JQuery<Element>, 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 = $(event.currentTarget);
|
var elem: JQuery<Element> = $(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 = $(Constants.htmlSelectors.dataTableAllRowsSelector);
|
var dataTableRows: JQuery<Element> = $(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 = dataTableRows.eq(safeIndex);
|
var selectedRowElement: JQuery<Element> = 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): Entities.ITableEntityIdentity {
|
private getEntityIdentity($elem: JQuery<Element>): Entities.ITableEntityIdentity {
|
||||||
return {
|
return {
|
||||||
RowKey: $elem.attr(Constants.htmlAttributeNames.dataTableRowKeyAttr),
|
RowKey: $elem.attr(Constants.htmlAttributeNames.dataTableRowKeyAttr),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateLastSelectedItem($elem: JQuery, isShiftSelect: boolean) {
|
private updateLastSelectedItem($elem: JQuery<Element>, 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) {
|
private applySingleSelection($elem: JQuery<Element>) {
|
||||||
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): void {
|
private applyCtrlSelection($elem: JQuery<Element>): 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): void {
|
private applyShiftSelection($elem: JQuery<Element>): 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) {
|
private applyContextMenuSelection($elem: JQuery<Element>) {
|
||||||
var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem);
|
var entityIdentity: Entities.ITableEntityIdentity = this.getEntityIdentity($elem);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
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";
|
||||||
@@ -13,7 +14,7 @@ export function getRowSelector(selectorSchema: Entities.IProperty[]): string {
|
|||||||
return QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector + selector;
|
return QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector + selector;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isRowVisible(dataTableScrollBodyQuery: JQuery, element: HTMLElement): boolean {
|
export function isRowVisible(dataTableScrollBodyQuery: JQuery<Element>, element: Element): boolean {
|
||||||
let isVisible = false;
|
let isVisible = false;
|
||||||
|
|
||||||
if (dataTableScrollBodyQuery.length && element) {
|
if (dataTableScrollBodyQuery.length && element) {
|
||||||
@@ -26,16 +27,18 @@ export function isRowVisible(dataTableScrollBodyQuery: JQuery, element: HTMLElem
|
|||||||
return isVisible;
|
return isVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scrollToRowIfNeeded(dataTableRows: JQuery, currentIndex: number, isScrollUp: boolean): void {
|
export function scrollToRowIfNeeded(dataTableRows: JQuery<Element>, currentIndex: number, isScrollUp: boolean): void {
|
||||||
if (dataTableRows.length) {
|
if (dataTableRows.length) {
|
||||||
const dataTableScrollBodyQuery: JQuery = $(QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector),
|
const dataTableScrollBodyQuery: JQuery<Element> = $(
|
||||||
selectedRowElement: HTMLElement = dataTableRows.get(currentIndex);
|
QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector,
|
||||||
|
),
|
||||||
|
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 = $(selectedRowElement),
|
const selectedRowQuery: JQuery<Element> = $(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;
|
||||||
@@ -54,8 +57,8 @@ export function scrollToRowIfNeeded(dataTableRows: JQuery, currentIndex: number,
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function scrollToTopIfNeeded(): void {
|
export function scrollToTopIfNeeded(): void {
|
||||||
const $dataTableRows: JQuery = $(QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector),
|
const $dataTableRows: JQuery<Element> = $(QueryBuilderConstants.htmlSelectors.dataTableAllRowsSelector),
|
||||||
$dataTableScrollBody: JQuery = $(QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector);
|
$dataTableScrollBody: JQuery<Element> = $(QueryBuilderConstants.htmlSelectors.dataTableScrollBodySelector);
|
||||||
|
|
||||||
if ($dataTableRows.length && $dataTableScrollBody.length) {
|
if ($dataTableRows.length && $dataTableScrollBody.length) {
|
||||||
$dataTableScrollBody.scrollTop(0);
|
$dataTableScrollBody.scrollTop(0);
|
||||||
@@ -71,7 +74,7 @@ export function setPaginationButtonEventHandlers(): void {
|
|||||||
.attr("role", "button");
|
.attr("role", "button");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterColumns(table: DataTables.DataTable, settings: boolean[]): void {
|
export function filterColumns(table: DataTables.Api<HTMLElement>, 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);
|
||||||
@@ -84,7 +87,7 @@ export function filterColumns(table: DataTables.DataTable, settings: boolean[]):
|
|||||||
* 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.DataTable,
|
table: DataTables.Api<HTMLElement>,
|
||||||
targetOrder: number[],
|
targetOrder: number[],
|
||||||
currentOrder?: number[],
|
currentOrder?: number[],
|
||||||
//eslint-disable-next-line
|
//eslint-disable-next-line
|
||||||
@@ -108,7 +111,9 @@ export function reorderColumns(
|
|||||||
? calculateTransformationOrder(currentOrder, targetOrder)
|
? calculateTransformationOrder(currentOrder, targetOrder)
|
||||||
: targetOrder;
|
: targetOrder;
|
||||||
try {
|
try {
|
||||||
$.fn.dataTable.ColReorder(table).fnOrder(transformationOrder);
|
// TODO: This possibly does not work with the new version of datatables.
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
@@ -116,9 +121,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.
|
||||||
@@ -133,8 +138,10 @@ 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.DataTable): number[] {
|
export function getCurrentOrder(table: DataTables.Api<HTMLElement>): number[] {
|
||||||
return $.fn.dataTable.ColReorder(table).fnOrder();
|
// TODO: This possibly does not work with the new version of datatables.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
return ($.fn.dataTable as any).ColReorder(table).fnOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,8 +185,8 @@ export function calculateTransformationOrder(currentOrder: number[], targetOrder
|
|||||||
return transformationOrder;
|
return transformationOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDataTableHeaders(table: DataTables.DataTable): string[] {
|
export function getDataTableHeaders(table: DataTables.Api<HTMLElement>): string[] {
|
||||||
const columns: DataTables.ColumnsMethods = table.columns();
|
const columns = 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
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import * as _ from "underscore";
|
import * as _ from "underscore";
|
||||||
|
|
||||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||||
import CacheBase from "./CacheBase";
|
import * as DataTables from "datatables.net";
|
||||||
import * as CommonConstants from "../../../Common/Constants";
|
import * as CommonConstants from "../../../Common/Constants";
|
||||||
|
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||||
|
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||||
|
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";
|
||||||
import QueryTablesTab from "../../Tabs/QueryTablesTab";
|
import CacheBase from "./CacheBase";
|
||||||
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.
|
||||||
@@ -27,7 +28,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.DataTable;
|
public table: DataTables.Api<HTMLElement>;
|
||||||
|
|
||||||
// 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;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
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";
|
||||||
@@ -131,7 +132,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.DataTable {
|
public reloadTable(useSetting: boolean = true, resetHeaders: boolean = true): DataTables.Api<Element> {
|
||||||
this.clearCache();
|
this.clearCache();
|
||||||
this.clearSelection();
|
this.clearSelection();
|
||||||
this.isCancelled = false;
|
this.isCancelled = false;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
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";
|
||||||
@@ -643,7 +644,7 @@ export default class QueryBuilderViewModel {
|
|||||||
return groupViewModels;
|
return groupViewModels;
|
||||||
};
|
};
|
||||||
|
|
||||||
public runQuery = (): DataTables.DataTable => {
|
public runQuery = (): DataTable.Api<Element> => {
|
||||||
return this._queryViewModel.runQuery();
|
return this._queryViewModel.runQuery();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
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 { useSidePanel } from "../../../hooks/useSidePanel";
|
|
||||||
import { userContext } from "../../../UserContext";
|
import { userContext } from "../../../UserContext";
|
||||||
|
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||||
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";
|
||||||
@@ -158,7 +159,7 @@ export default class QueryViewModel {
|
|||||||
notify: "always",
|
notify: "always",
|
||||||
});
|
});
|
||||||
|
|
||||||
public runQuery = (): DataTables.DataTable => {
|
public runQuery = (): DataTables.Api<Element> => {
|
||||||
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, "'");
|
||||||
@@ -176,7 +177,7 @@ export default class QueryViewModel {
|
|||||||
return this._tableEntityListViewModel.reloadTable(/*useSetting*/ false, /*resetHeaders*/ false);
|
return this._tableEntityListViewModel.reloadTable(/*useSetting*/ false, /*resetHeaders*/ false);
|
||||||
};
|
};
|
||||||
|
|
||||||
public clearQuery = (): DataTables.DataTable => {
|
public clearQuery = (): DataTables.Api<Element> => {
|
||||||
this.queryText();
|
this.queryText();
|
||||||
this.topValue();
|
this.topValue();
|
||||||
this.selectText();
|
this.selectText();
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ export class CassandraAPIDataClient extends TableDataClient {
|
|||||||
query,
|
query,
|
||||||
paginationToken,
|
paginationToken,
|
||||||
},
|
},
|
||||||
beforeSend: this.setAuthorizationHeader,
|
beforeSend: this.setAuthorizationHeader as any,
|
||||||
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,
|
beforeSend: this.setAuthorizationHeader as any,
|
||||||
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,
|
beforeSend: this.setAuthorizationHeader as any,
|
||||||
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,
|
beforeSend: this.setAuthorizationHeader as any,
|
||||||
cache: false,
|
cache: false,
|
||||||
}).then(
|
}).then(
|
||||||
(data: any) => {
|
(data: any) => {
|
||||||
|
|||||||
@@ -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, nodes, enablePublicIpAccess } = userContext.postgresConnectionStrParams;
|
const { adminLogin, databaseName, 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}/citus?sslmode=require`;
|
const postgresSQLConnectionURL = `postgres://${adminLogin}:{your_password}@${selectedNode}:${portNumber}/${databaseName}?sslmode=require`;
|
||||||
const psql = `psql "host=${selectedNode} port=${portNumber} dbname=citus user=${adminLogin} password={your_password} sslmode=require"`;
|
const psql = `psql "host=${selectedNode} port=${portNumber} dbname=${databaseName} user=${adminLogin} password={your_password} sslmode=require"`;
|
||||||
const jdbc = `jdbc:postgresql://${selectedNode}:${portNumber}/citus?user=${adminLogin}&password={your_password}&sslmode=require`;
|
const jdbc = `jdbc:postgresql://${selectedNode}:${portNumber}/${databaseName}?user=${adminLogin}&password={your_password}&sslmode=require`;
|
||||||
const libpq = `host=${selectedNode} port=${portNumber} dbname=citus user=${adminLogin} password={your_password} sslmode=require`;
|
const libpq = `host=${selectedNode} port=${portNumber} dbname=${databaseName} user=${adminLogin} password={your_password} sslmode=require`;
|
||||||
const adoDotNet = `Server=${selectedNode};Database=citus;Port=${portNumber};User Id=${adminLogin};Password={your_password};Ssl Mode=Require;`;
|
const adoDotNet = `Server=${selectedNode};Database=${databaseName};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 }}>
|
||||||
|
|||||||
@@ -444,7 +444,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: "Copilot",
|
ariaLabel: this.state.copilotActive ? "Disable Copilot" : "Enable Copilot",
|
||||||
hasPopup: false,
|
hasPopup: false,
|
||||||
};
|
};
|
||||||
buttons.push(toggleCopilotButton);
|
buttons.push(toggleCopilotButton);
|
||||||
|
|||||||
@@ -769,7 +769,10 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
|
|||||||
|
|
||||||
const dataRootNode = buildDataTree();
|
const dataRootNode = buildDataTree();
|
||||||
const isSampleDataEnabled =
|
const isSampleDataEnabled =
|
||||||
useQueryCopilot().copilotEnabled && userContext.sampleDataConnectionInfo && userContext.apiType === "SQL";
|
useQueryCopilot().copilotEnabled &&
|
||||||
|
useQueryCopilot().copilotSampleDBEnabled &&
|
||||||
|
userContext.sampleDataConnectionInfo &&
|
||||||
|
userContext.apiType === "SQL";
|
||||||
const sampleDataResourceTokenCollection = useDatabases((state) => state.sampleDataResourceTokenCollection);
|
const sampleDataResourceTokenCollection = useDatabases((state) => state.sampleDataResourceTokenCollection);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export enum StorageKey {
|
|||||||
MaxWaitTimeInSeconds,
|
MaxWaitTimeInSeconds,
|
||||||
AutomaticallyCancelQueryAfterTimeout,
|
AutomaticallyCancelQueryAfterTimeout,
|
||||||
ContainerPaginationEnabled,
|
ContainerPaginationEnabled,
|
||||||
|
CopilotSampleDBEnabled,
|
||||||
CustomItemPerPage,
|
CustomItemPerPage,
|
||||||
DatabaseAccountId,
|
DatabaseAccountId,
|
||||||
EncryptedKeyToken,
|
EncryptedKeyToken,
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ 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;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ 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;
|
||||||
@@ -28,6 +29,7 @@ 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;
|
||||||
@@ -50,6 +52,7 @@ 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;
|
||||||
@@ -69,6 +72,7 @@ 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;
|
||||||
@@ -91,11 +95,12 @@ export interface QueryCopilotState {
|
|||||||
resetQueryCopilotStates: () => void;
|
resetQueryCopilotStates: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryCopilotStore = UseStore<QueryCopilotState>;
|
type QueryCopilotStore = UseStore<Partial<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: "",
|
||||||
@@ -104,7 +109,7 @@ export const useQueryCopilot: QueryCopilotStore = create((set) => ({
|
|||||||
correlationId: "",
|
correlationId: "",
|
||||||
query: "SELECT * FROM c",
|
query: "SELECT * FROM c",
|
||||||
selectedQuery: "",
|
selectedQuery: "",
|
||||||
isGeneratingQuery: false,
|
isGeneratingQuery: null,
|
||||||
isGeneratingExplanation: false,
|
isGeneratingExplanation: false,
|
||||||
isExecuting: false,
|
isExecuting: false,
|
||||||
dislikeQuery: undefined,
|
dislikeQuery: undefined,
|
||||||
@@ -145,6 +150,7 @@ 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 }),
|
||||||
|
|||||||
Reference in New Issue
Block a user