diff --git a/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx b/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx index 5107bc6da..0109927d5 100644 --- a/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx +++ b/src/Explorer/Panes/Tables/AddTableEntityPanel.tsx @@ -14,7 +14,8 @@ import * as Entities from "../../Tables/Entities"; import { CassandraAPIDataClient, CassandraTableKey, TableDataClient } from "../../Tables/TableDataClient"; import * as TableEntityProcessor from "../../Tables/TableEntityProcessor"; import * as Utilities from "../../Tables/Utilities"; -import QueryTablesTab from "../../Tabs/QueryTablesTab"; +import NewQueryTablesTab from "../../Tabs/QueryTablesTab/QueryTablesTab"; +// import QueryTablesTab from "../../Tabs/QueryTablesTab"; import { PanelContainerComponent } from "../PanelContainerComponent"; import { attributeNameLabel, @@ -38,7 +39,8 @@ import { interface AddTableEntityPanelProps { tableDataClient: TableDataClient; - queryTablesTab: QueryTablesTab; + queryTablesTab: NewQueryTablesTab; + // queryTablesTab: QueryTablesTab; tableEntityListViewModel: TableEntityListViewModel; cassandraApiClient: CassandraAPIDataClient; } diff --git a/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx b/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx index f71a1a829..bd29288b5 100644 --- a/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx +++ b/src/Explorer/Panes/Tables/EditTableEntityPanel.tsx @@ -13,7 +13,8 @@ import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListView import * as Entities from "../../Tables/Entities"; import { CassandraAPIDataClient, TableDataClient } from "../../Tables/TableDataClient"; import * as TableEntityProcessor from "../../Tables/TableEntityProcessor"; -import QueryTablesTab from "../../Tabs/QueryTablesTab"; +import NewQueryTablesTab from "../../Tabs/QueryTablesTab/QueryTablesTab"; +// import QueryTablesTab from "../../Tabs/QueryTablesTab"; import { PanelContainerComponent } from "../PanelContainerComponent"; import { attributeNameLabel, @@ -35,7 +36,8 @@ import { interface EditTableEntityPanelProps { tableDataClient: TableDataClient; - queryTablesTab: QueryTablesTab; + queryTablesTab: NewQueryTablesTab; + // queryTablesTab: QueryTablesTab; tableEntityListViewModel: TableEntityListViewModel; cassandraApiClient: CassandraAPIDataClient; } diff --git a/src/Explorer/Tables/DataTable/DataTableViewModel.ts b/src/Explorer/Tables/DataTable/DataTableViewModel.ts index 4afcbff29..5815cc6db 100644 --- a/src/Explorer/Tables/DataTable/DataTableViewModel.ts +++ b/src/Explorer/Tables/DataTable/DataTableViewModel.ts @@ -1,14 +1,14 @@ +import { ItemDefinition, QueryIterator, Resource } from "@azure/cosmos"; import * as ko from "knockout"; import * as _ from "underscore"; - -import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; -import CacheBase from "./CacheBase"; import * as CommonConstants from "../../../Common/Constants"; +import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; +// import QueryTablesTab from "../../Tabs/QueryTablesTab"; +import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; +import NewQueryTablesTab from "../../Tabs/QueryTablesTab/QueryTablesTab"; import * as Constants from "../Constants"; import * as Entities from "../Entities"; -import QueryTablesTab from "../../Tabs/QueryTablesTab"; -import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; -import { QueryIterator, ItemDefinition, Resource } from "@azure/cosmos"; +import CacheBase from "./CacheBase"; // 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. @@ -49,7 +49,8 @@ abstract class DataTableViewModel { private dataTableOperationManager: IDataTableOperation; - public queryTablesTab: QueryTablesTab; + // public queryTablesTab: QueryTablesTab; + public queryTablesTab: NewQueryTablesTab; constructor() { this.items([]); diff --git a/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts b/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts index f11730b2b..cbacfab01 100644 --- a/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts +++ b/src/Explorer/Tables/DataTable/TableEntityListViewModel.ts @@ -6,7 +6,8 @@ import * as ViewModels from "../../../Contracts/ViewModels"; import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import { userContext } from "../../../UserContext"; -import QueryTablesTab from "../../Tabs/QueryTablesTab"; +// import QueryTablesTab from "../../Tabs/QueryTablesTab"; +import NewQueryTablesTab from "../../Tabs/QueryTablesTab/QueryTablesTab"; import * as Constants from "../Constants"; import { getQuotedCqlIdentifier } from "../CqlUtilities"; import * as Entities from "../Entities"; @@ -101,7 +102,9 @@ export default class TableEntityListViewModel extends DataTableViewModel { public useSetting: boolean = true; //public tableExplorerContext: TableExplorerContext; - public notifyColumnChanges: (enablePrompt: boolean, queryTablesTab: QueryTablesTab) => void; + public notifyColumnChanges: (enablePrompt: boolean, queryTablesTab: NewQueryTablesTab) => void; + // public notifyColumnChanges: (enablePrompt: boolean, queryTablesTab: QueryTablesTab) => void; + public tablePageStartIndex: number; public tableQuery: Entities.ITableQuery = {}; public cqlQuery: ko.Observable; @@ -112,7 +115,8 @@ export default class TableEntityListViewModel extends DataTableViewModel { public queryErrorMessage: ko.Observable; public id: string; - constructor(tableCommands: TableCommands, queryTablesTab: QueryTablesTab) { + // constructor(tableCommands: TableCommands, queryTablesTab: QueryTablesTab) { + constructor(tableCommands: TableCommands, queryTablesTab: NewQueryTablesTab) { super(); this.cache = new TableEntityCache(); this.queryErrorMessage = ko.observable(); diff --git a/src/Explorer/Tables/QueryBuilder/QueryViewModel.tsx b/src/Explorer/Tables/QueryBuilder/QueryViewModel.tsx index f816cad60..33b375089 100644 --- a/src/Explorer/Tables/QueryBuilder/QueryViewModel.tsx +++ b/src/Explorer/Tables/QueryBuilder/QueryViewModel.tsx @@ -5,7 +5,7 @@ import { KeyCodes } from "../../../Common/Constants"; import { useSidePanel } from "../../../hooks/useSidePanel"; import { userContext } from "../../../UserContext"; import { TableQuerySelectPanel } from "../../Panes/Tables/TableQuerySelectPanel/TableQuerySelectPanel"; -import QueryTablesTab from "../../Tabs/QueryTablesTab"; +import NewQueryTablesTab from "../../Tabs/QueryTablesTab/QueryTablesTab"; import { getQuotedCqlIdentifier } from "../CqlUtilities"; import * as DataTableUtilities from "../DataTable/DataTableUtilities"; import TableEntityListViewModel from "../DataTable/TableEntityListViewModel"; @@ -39,14 +39,16 @@ export default class QueryViewModel { public columnOptions: ko.ObservableArray; - public queryTablesTab: QueryTablesTab; + public queryTablesTab: NewQueryTablesTab; + // public queryTablesTab: QueryTablesTab; public id: string; private _tableEntityListViewModel: TableEntityListViewModel; - constructor(queryTablesTab: QueryTablesTab) { + // constructor(queryTablesTab: QueryTablesTab) { + constructor(queryTablesTab: NewQueryTablesTab) { this.queryTablesTab = queryTablesTab; this.id = `queryViewModel${this.queryTablesTab.tabId}`; - this._tableEntityListViewModel = queryTablesTab.tableEntityListViewModel(); + this._tableEntityListViewModel = queryTablesTab.tableEntityListViewModel; this.queryTextIsReadOnly = ko.computed(() => { return userContext.apiType !== "Cassandra"; diff --git a/src/Explorer/Tabs/QueryTablesTab/QueryTablesTab.tsx b/src/Explorer/Tabs/QueryTablesTab/QueryTablesTab.tsx new file mode 100644 index 000000000..6d19024b0 --- /dev/null +++ b/src/Explorer/Tabs/QueryTablesTab/QueryTablesTab.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import * as ViewModels from "../../../Contracts/ViewModels"; +import Explorer from "../../Explorer"; +import TableCommands from "../../Tables/DataTable/TableCommands"; +import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel"; +import TabsBase from "../TabsBase"; +import QueryTablesTabComponent, { IQueryTablesTabComponentProps } from "./QueryTablesTabComponent"; + +interface QueryTablesTabProps { + container: Explorer; +} + +class NewQueryTablesTab extends TabsBase { + public iQueryTablesTabCompProps: IQueryTablesTabComponentProps; + public collection: ViewModels.Collection; + public tableEntityListViewModel: TableEntityListViewModel; + public tableCommands: TableCommands; + + constructor(options: ViewModels.TabOptions, props: QueryTablesTabProps) { + super(options); + this.tableCommands = new TableCommands(props.container); + this.tableEntityListViewModel = new TableEntityListViewModel(this.tableCommands, this); + this.iQueryTablesTabCompProps = { + tabKind: options.tabKind, + title: options.title, + tabPath: options.tabPath, + collection: options.collection, + node: options.node, + onLoadStartKey: options.onLoadStartKey, + container: props.container, + tabsBaseInstance: this, + queryTablesTab: this, + }; + console.log("🚀 ~ file: QueryTablesTab.tsx ~ line 13 ~ NewQueryTablesTab ~ constructor ~ props", props); + } + public render(): JSX.Element { + return ; + } +} + +export default NewQueryTablesTab; diff --git a/src/Explorer/Tabs/QueryTablesTab/QueryTablesTabComponent.tsx b/src/Explorer/Tabs/QueryTablesTab/QueryTablesTabComponent.tsx new file mode 100644 index 000000000..791db5b86 --- /dev/null +++ b/src/Explorer/Tabs/QueryTablesTab/QueryTablesTabComponent.tsx @@ -0,0 +1,429 @@ +import React, { Component } from "react"; +import QueryInformation from "../../../../images//QueryBuilder/QueryInformation_16x.png"; +import AddProperty from "../../../../images/Add-property.svg"; +import AddEntityIcon from "../../../../images/AddEntity.svg"; +import AndOr from "../../../../images/And-Or.svg"; +import DeleteEntitiesIcon from "../../../../images/DeleteEntities.svg"; +import EditEntityIcon from "../../../../images/Edit-entity.svg"; +import ErrorRed from "../../../../images/error_red.svg"; +import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg"; +import QueryBuilderIcon from "../../../../images/Query-Builder.svg"; +import QueryTextIcon from "../../../../images/Query-Text.svg"; +import StatusWraning from "../../../../images/QueryBuilder/StatusWarning_16x.png"; +import TriangleDown from "../../../../images/Triangle-down.svg"; +import TriangleRight from "../../../../images/Triangle-right.svg"; +import * as ViewModels from "../../../Contracts/ViewModels"; +import { useSidePanel } from "../../../hooks/useSidePanel"; +import { userContext } from "../../../UserContext"; +import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent"; +import Explorer from "../../Explorer"; +import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter"; +import { AddTableEntityPanel } from "../../Panes/Tables/AddTableEntityPanel"; +import { EditTableEntityPanel } from "../../Panes/Tables/EditTableEntityPanel"; +import TableCommands from "../../Tables/DataTable/TableCommands"; +import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel"; +import QueryViewModel from "../../Tables/QueryBuilder/QueryViewModel"; +import { CassandraAPIDataClient, TableDataClient } from "../../Tables/TableDataClient"; +import TabsBase from "../TabsBase"; +import NewQueryTablesTab from "./QueryTablesTab"; +export interface Button { + visible: boolean; + enabled: boolean; + isSelected?: boolean; +} + +export interface IQueryTablesTabComponentProps { + tabKind: ViewModels.CollectionTabKind; + title: string; + tabPath: string; + collection: ViewModels.CollectionBase; + node: ViewModels.TreeNode; + onLoadStartKey: number; + container: Explorer; + tabsBaseInstance: TabsBase; + queryTablesTab: NewQueryTablesTab; +} + +interface IQueryTablesTabComponentStates { + tableEntityListViewModel: TableEntityListViewModel; + queryViewModel: QueryViewModel; + queryText: string; + selectedQueryText: string; + executeQueryButton: Button; + queryBuilderButton: Button; + queryTextButton: Button; + addEntityButton: Button; + editEntityButton: Button; + deleteEntityButton: Button; +} + +class QueryTablesTabComponent extends Component { + // public readonly html = template; + public collection: ViewModels.Collection; + // public tableEntityListViewModel = ko.observable(); + // public queryViewModel = ko.observable(); + public tableCommands: TableCommands; + public tableDataClient: TableDataClient; + + // public queryText = ko.observable("PartitionKey eq 'partitionKey1'"); // Start out with an example they can modify + // public selectedQueryText = ko.observable("").extend({ notify: "always" }); + + public executeQueryButton: ViewModels.Button; + public addEntityButton: ViewModels.Button; + public editEntityButton: ViewModels.Button; + public deleteEntityButton: ViewModels.Button; + public queryBuilderButton: ViewModels.Button; + public queryTextButton: ViewModels.Button; + public container: Explorer; + constructor(props: IQueryTablesTabComponentProps) { + super(props); + this.container = props.collection && props.collection.container; + this.tableCommands = new TableCommands(this.container); + this.tableDataClient = this.container.tableDataClient; + + this.state = { + tableEntityListViewModel: new TableEntityListViewModel(this.tableCommands, props.queryTablesTab), + // tableEntityListViewModel.queryTablesTab : this.props.queryTablesTab + queryViewModel: new QueryViewModel(this.props.queryTablesTab), + queryText: "PartitionKey eq 'partionKey1'", + selectedQueryText: "", + executeQueryButton: { + enabled: true, + visible: true, + isSelected: false, + }, + queryBuilderButton: { + enabled: true, + visible: true, + isSelected: false, + }, + queryTextButton: { + enabled: true, + visible: true, + isSelected: false, + }, + addEntityButton: { + enabled: true, + visible: true, + isSelected: false, + }, + editEntityButton: { + enabled: true, + visible: true, + isSelected: false, + }, + deleteEntityButton: { + enabled: true, + visible: true, + isSelected: false, + }, + }; + // console.log( + // "🚀 ~ file: QueryTablesTabComponent.tsx ~ line 24 ~ QueryTablesTabComponent ~ constructor ~ props", + // props + // ); + useCommandBar.getState().setContextButtons(this.getTabsButtons()); + + this.buildCommandBarOptions(); + } + + public onAddEntityClick = (): void => { + useSidePanel + .getState() + .openSidePanel( + "Add Table Entity", + + ); + }; + + public onEditEntityClick = (): void => { + useSidePanel + .getState() + .openSidePanel( + "Edit Table Entity", + + ); + }; + + public onDeleteEntityClick = (): void => { + this.tableCommands.deleteEntitiesCommand(this.state.tableEntityListViewModel); + }; + + protected getTabsButtons(): CommandButtonComponentProps[] { + const buttons: CommandButtonComponentProps[] = []; + if (this.state.queryBuilderButton.visible) { + const label = userContext.apiType === "Cassandra" ? "CQL Query Builder" : "Query Builder"; + buttons.push({ + iconSrc: QueryBuilderIcon, + iconAlt: label, + onCommandClick: () => this.state.queryViewModel.selectHelper(), + commandButtonLabel: label, + ariaLabel: label, + hasPopup: false, + disabled: !this.state.queryBuilderButton.enabled, + isSelected: this.state.queryBuilderButton.isSelected, + }); + } + + if (this.state.queryTextButton.visible) { + const label = userContext.apiType === "Cassandra" ? "CQL Query Text" : "Query Text"; + buttons.push({ + iconSrc: QueryTextIcon, + iconAlt: label, + onCommandClick: () => this.state.queryViewModel.selectEditor(), + commandButtonLabel: label, + ariaLabel: label, + hasPopup: false, + disabled: !this.state.queryTextButton.enabled, + isSelected: this.state.queryTextButton.isSelected, + }); + } + + if (this.state.executeQueryButton.visible) { + const label = "Run Query"; + buttons.push({ + iconSrc: ExecuteQueryIcon, + iconAlt: label, + onCommandClick: () => this.state.queryViewModel.runQuery(), + commandButtonLabel: label, + ariaLabel: label, + hasPopup: false, + disabled: !this.state.executeQueryButton.enabled, + }); + } + + if (this.state.addEntityButton.visible) { + const label = userContext.apiType === "Cassandra" ? "Add Row" : "Add Entity"; + buttons.push({ + iconSrc: AddEntityIcon, + iconAlt: label, + onCommandClick: this.onAddEntityClick, + commandButtonLabel: label, + ariaLabel: label, + hasPopup: true, + disabled: !this.state.addEntityButton.enabled, + }); + } + + if (this.state.editEntityButton.visible) { + const label = userContext.apiType === "Cassandra" ? "Edit Row" : "Edit Entity"; + buttons.push({ + iconSrc: EditEntityIcon, + iconAlt: label, + onCommandClick: this.onEditEntityClick, + commandButtonLabel: label, + ariaLabel: label, + hasPopup: true, + disabled: !this.state.editEntityButton.enabled, + }); + } + + if (this.state.deleteEntityButton.visible) { + const label = userContext.apiType === "Cassandra" ? "Delete Rows" : "Delete Entities"; + buttons.push({ + iconSrc: DeleteEntitiesIcon, + iconAlt: label, + onCommandClick: this.onDeleteEntityClick, + commandButtonLabel: label, + ariaLabel: label, + hasPopup: true, + disabled: !this.state.deleteEntityButton.enabled, + }); + } + return buttons; + } + + protected buildCommandBarOptions(): void { + this.props.tabsBaseInstance.updateNavbarWithTabsButtons(); + } + + render(): JSX.Element { + useCommandBar.getState().setContextButtons(this.getTabsButtons()); + + return ( +
+
+
+
+ + + + +
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+
+
+
+ + Add new clause + + +
+
+
+
+
+
+ +
+ toggle +
+
+ toggle +
+ Advanced Options +
+
+
+
+ Show top results: + +
+ + +
+
+
+ Select fields for query: +
+ + +
+ + Choose Columns... + +
+
+
+
+
+
+
+
+ ); + } +} + +export default QueryTablesTabComponent; diff --git a/src/Explorer/Tree/Collection.ts b/src/Explorer/Tree/Collection.ts index c486da03e..bf164f563 100644 --- a/src/Explorer/Tree/Collection.ts +++ b/src/Explorer/Tree/Collection.ts @@ -32,7 +32,8 @@ import MongoDocumentsTab from "../Tabs/MongoDocumentsTab"; import { NewMongoQueryTab } from "../Tabs/MongoQueryTab/MongoQueryTab"; import { NewMongoShellTab } from "../Tabs/MongoShellTab/MongoShellTab"; import { NewQueryTab } from "../Tabs/QueryTab/QueryTab"; -import QueryTablesTab from "../Tabs/QueryTablesTab"; +// import QueryTablesTab from "../Tabs/QueryTablesTab"; +import NewQueryTablesTab from "../Tabs/QueryTablesTab/QueryTablesTab"; import { CollectionSettingsTabV2 } from "../Tabs/SettingsTabV2"; import { useDatabases } from "../useDatabases"; import { useSelectedNode } from "../useSelectedNode"; @@ -391,13 +392,13 @@ export default class Collection implements ViewModels.Collection { }); } - const queryTablesTabs: QueryTablesTab[] = useTabs + const queryTablesTabs: NewQueryTablesTab[] = useTabs .getState() .getTabs( ViewModels.CollectionTabKind.QueryTables, (tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id() - ) as QueryTablesTab[]; - let queryTablesTab: QueryTablesTab = queryTablesTabs && queryTablesTabs[0]; + ) as NewQueryTablesTab[]; + let queryTablesTab: NewQueryTablesTab = queryTablesTabs && queryTablesTabs[0]; if (queryTablesTab) { useTabs.getState().activateTab(queryTablesTab); @@ -415,14 +416,57 @@ export default class Collection implements ViewModels.Collection { tabTitle: title, }); - queryTablesTab = new QueryTablesTab({ - tabKind: ViewModels.CollectionTabKind.QueryTables, - title: title, - tabPath: "", - collection: this, - node: this, - onLoadStartKey: startKey, - }); + queryTablesTab = new NewQueryTablesTab( + { + tabKind: ViewModels.CollectionTabKind.QueryTables, + title: title, + tabPath: "", + collection: this, + node: this, + onLoadStartKey: startKey, + }, + { + container: this.container, + } + ); + + // const queryTablesTabs: QueryTablesTab[] = useTabs + // .getState() + // .getTabs( + // ViewModels.CollectionTabKind.QueryTables, + // (tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id() + // ) as QueryTablesTab[]; + // let queryTablesTab: QueryTablesTab = queryTablesTabs && queryTablesTabs[0]; + + // if (queryTablesTab) { + // useTabs.getState().activateTab(queryTablesTab); + // } else { + // this.documentIds([]); + // let title = `Entities`; + // if (userContext.apiType === "Cassandra") { + // title = `Rows`; + // } + // const startKey: number = TelemetryProcessor.traceStart(Action.Tab, { + // databaseName: this.databaseId, + // collectionName: this.id(), + + // dataExplorerArea: Constants.Areas.Tab, + // tabTitle: title, + // }); + + // queryTablesTab = new QueryTablesTab( + // { + // tabKind: ViewModels.CollectionTabKind.QueryTables, + // title: title, + // tabPath: "", + // collection: this, + // node: this, + // onLoadStartKey: startKey, + // }, + // { + // container: this.container, + // } + // ); useTabs.getState().activateNewTab(queryTablesTab); }