mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-27 04:41:48 +00:00
Compare commits
3 Commits
2276933def
...
users/v-da
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88b1265ce9 | ||
|
|
b1a434c011 | ||
|
|
bc951b4e13 |
3
images/CopilotQueryTablesTab.svg
Normal file
3
images/CopilotQueryTablesTab.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="22" viewBox="0 0 20 29" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5849 1.28251C11.0125 0.262601 8.98746 0.2626 7.41509 1.28251L2.16509 4.68792C0.8149 5.56372 0 7.06362 0 8.67298V13.3266C0 14.9359 0.814898 16.4358 2.16509 17.3116L7.41509 20.717C8.98746 21.737 11.0125 21.737 12.5849 20.717L17.8349 17.3116C19.1851 16.4358 20 14.9359 20 13.3266V8.67298C20 7.06362 19.1851 5.56372 17.8349 4.68792L12.5849 1.28251ZM9.50097 2.05606C10.2759 1.93579 11.0848 2.09742 11.7686 2.54095L17.0186 5.94636C17.9424 6.54559 18.5 7.57184 18.5 8.67298V11.3953C18.287 11.1924 18.0465 11.0115 17.7802 10.8583L11.8647 7.45689C10.7104 6.79321 9.29086 6.79131 8.13486 7.45187L7.5 7.81465V4.41921C7.5 3.54159 8.01028 2.74407 8.80712 2.3763L9.50097 2.05606ZM17.8644 15.2255L17.7932 15.3501C17.5776 15.6212 17.3172 15.8595 17.0186 16.0532L11.7686 19.4586C10.6928 20.1564 9.30721 20.1564 8.23138 19.4586L5.86807 17.9256C6.11557 17.8358 6.35595 17.719 6.58487 17.5752L12.2457 14.0169C13.3374 13.3307 14 12.1316 14 10.8421V10.415L17.0324 12.1587C18.1077 12.7769 18.4798 14.1486 17.8644 15.2255ZM12.5 9.55251V10.8421C12.5 11.6158 12.1025 12.3352 11.4474 12.747L10.0303 13.6377L8.57078 12.7396C7.90535 12.3301 7.5 11.6047 7.5 10.8233V9.54228L8.87907 8.75424C9.57267 8.3579 10.4244 8.35904 11.1169 8.75725L12.5 9.55251ZM8.61445 14.5277L5.78662 16.3052C5.02045 16.7868 4.04031 16.7625 3.29894 16.2435L2.5521 15.7207C1.88767 15.1109 1.5 14.2448 1.5 13.3266V8.67298C1.5 7.57184 2.05756 6.54559 2.98138 5.94636L6.0268 3.97095C6.00907 4.11852 6 4.26815 6 4.41921V10.8233C6 12.1256 6.67558 13.3345 7.78463 14.017L8.61445 14.5277Z" fill="#0078D4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -650,4 +650,4 @@ tr:hover td.nameColumnText {
|
||||
|
||||
.context-menu-item.icon-customize-columns {
|
||||
background-image: url(../../images/Options.svg);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { DatePicker, TextField } from "@fluentui/react";
|
||||
import React, { FunctionComponent } from "react";
|
||||
import { attributeValueLabel } from "../Explorer/Panes/Tables/Validators/EntityTableHelper";
|
||||
|
||||
export interface TableEntityProps {
|
||||
entityValueLabel?: string;
|
||||
@@ -61,7 +60,6 @@ export const EntityValue: FunctionComponent<TableEntityProps> = ({
|
||||
placeholder={entityValuePlaceholder}
|
||||
value={typeof entityValue === "string" ? entityValue : ""}
|
||||
onChange={onEntityValueChange}
|
||||
ariaLabel={attributeValueLabel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
import React, { FunctionComponent } from "react";
|
||||
import DeleteIcon from "../../images/delete.svg";
|
||||
import EditIcon from "../../images/Edit_entity.svg";
|
||||
import { attributeNameLabel, dataTypeLabel } from "../Explorer/Panes/Tables/Validators/EntityTableHelper";
|
||||
import { CassandraType, TableType } from "../Explorer/Tables/Constants";
|
||||
import { userContext } from "../UserContext";
|
||||
import { EntityValue } from "./EntityValue";
|
||||
@@ -34,10 +33,10 @@ export interface TableEntityProps {
|
||||
isPropertyTypeDisable: boolean;
|
||||
entityTimeValue: string;
|
||||
isEntityValueDisable?: boolean;
|
||||
onDeleteEntity: () => void;
|
||||
onEditEntity: () => void;
|
||||
onDeleteEntity?: () => void;
|
||||
onEditEntity?: () => void;
|
||||
onEntityPropertyChange: (event: React.FormEvent<HTMLElement>, newInput?: string) => void;
|
||||
onEntityTypeChange: (event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption<any> | undefined) => void;
|
||||
onEntityTypeChange: (event: React.FormEvent<HTMLElement>, selectedParam: IDropdownOption) => void;
|
||||
onEntityValueChange: (event: React.FormEvent<HTMLElement>, newInput?: string) => void;
|
||||
onSelectDate: (date: Date | null | undefined) => void;
|
||||
onEntityTimeValueChange: (event: React.FormEvent<HTMLElement>, newInput?: string) => void;
|
||||
@@ -114,7 +113,6 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
value={entityProperty}
|
||||
onChange={onEntityPropertyChange}
|
||||
required
|
||||
ariaLabel={attributeNameLabel}
|
||||
/>
|
||||
<Dropdown
|
||||
label={entityTypeLabel && entityTypeLabel}
|
||||
@@ -124,7 +122,6 @@ export const TableEntity: FunctionComponent<TableEntityProps> = ({
|
||||
disabled={isPropertyTypeDisable}
|
||||
id="entityTypeId"
|
||||
styles={dropdownStyles}
|
||||
ariaLabel={dataTypeLabel}
|
||||
/>
|
||||
<EntityValue
|
||||
entityValueLabel={entityValueLabel}
|
||||
|
||||
@@ -133,10 +133,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
if (isDirty(this.props.maxAutoPilotThroughput, this.props.maxAutoPilotThroughputBaseline)) {
|
||||
isDiscardable = true;
|
||||
if (
|
||||
this.props.softAllowedMaximumThroughput
|
||||
? this.props.maxAutoPilotThroughput <= this.props.softAllowedMaximumThroughput &&
|
||||
AutoPilotUtils.isValidAutoPilotThroughput(this.props.maxAutoPilotThroughput)
|
||||
: AutoPilotUtils.isValidAutoPilotThroughput(this.props.maxAutoPilotThroughput)
|
||||
this.props.maxAutoPilotThroughput <= this.props.softAllowedMaximumThroughput &&
|
||||
AutoPilotUtils.isValidAutoPilotThroughput(this.props.maxAutoPilotThroughput)
|
||||
) {
|
||||
isSaveable = true;
|
||||
}
|
||||
|
||||
@@ -205,7 +205,6 @@ export const NewVertexComponent: FunctionComponent<INewVertexComponentProps> = (
|
||||
role="button"
|
||||
onClick={onAddNewProperty}
|
||||
onKeyPress={(event: React.KeyboardEvent<HTMLSpanElement>) => onAddNewPropertyKeyPress(event)}
|
||||
aria-label="Add property"
|
||||
>
|
||||
<img className="refreshcol rightPaneAddPropertyImg" src={AddIcon} alt="Add property" /> Add Property
|
||||
</span>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Text, TextField } from "@fluentui/react";
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import { Areas } from "Common/Constants";
|
||||
import { deleteDatabase } from "Common/dataAccess/deleteDatabase";
|
||||
import DeleteFeedback from "Common/DeleteFeedback";
|
||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||
import { deleteDatabase } from "Common/dataAccess/deleteDatabase";
|
||||
import { Collection, Database } from "Contracts/ViewModels";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
|
||||
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { useTabs } from "hooks/useTabs";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import { useDatabases } from "../useDatabases";
|
||||
import { useSelectedNode } from "../useSelectedNode";
|
||||
import { PanelInfoErrorComponent, PanelInfoErrorProps } from "./PanelInfoErrorComponent";
|
||||
@@ -36,13 +36,8 @@ export const DeleteDatabaseConfirmationPanel: FunctionComponent<DeleteDatabaseCo
|
||||
|
||||
const submit = async (): Promise<void> => {
|
||||
if (selectedDatabase?.id() && databaseInput !== selectedDatabase.id()) {
|
||||
setFormError(
|
||||
`Input database name "${databaseInput}" does not match the selected database "${selectedDatabase.id()}"`
|
||||
);
|
||||
setFormError("Input database name does not match the selected database");
|
||||
logConsoleError(`Error while deleting collection ${selectedDatabase && selectedDatabase.id()}`);
|
||||
logConsoleError(
|
||||
`Input database name "${databaseInput}" does not match the selected database "${selectedDatabase.id()}"`
|
||||
);
|
||||
return;
|
||||
}
|
||||
setFormError("");
|
||||
|
||||
@@ -295,9 +295,8 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
|
||||
className="addButtonEntiy"
|
||||
tabIndex={0}
|
||||
onKeyPress={handlekeypressaddentity}
|
||||
aria-label="Add Property"
|
||||
>
|
||||
<Image {...imageProps} src={AddPropertyIcon} alt="Add Property" />
|
||||
<Image {...imageProps} src={AddPropertyIcon} alt="Add Entity" />
|
||||
<Text className="addNewParamStyle">{getAddButtonLabel(userContext.apiType)}</Text>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
@@ -26,7 +26,6 @@ exports[`Excute Add Table Entity Pane should render Default properly 1`] = `
|
||||
className="panelMainContent"
|
||||
>
|
||||
<Stack
|
||||
aria-label="Add Property"
|
||||
className="addButtonEntiy"
|
||||
horizontal={true}
|
||||
onClick={[Function]}
|
||||
@@ -34,21 +33,20 @@ exports[`Excute Add Table Entity Pane should render Default properly 1`] = `
|
||||
tabIndex={0}
|
||||
>
|
||||
<div
|
||||
aria-label="Add Property"
|
||||
className="ms-Stack addButtonEntiy css-53"
|
||||
onClick={[Function]}
|
||||
onKeyPress={[Function]}
|
||||
tabIndex={0}
|
||||
>
|
||||
<StyledImageBase
|
||||
alt="Add Property"
|
||||
alt="Add Entity"
|
||||
height={30}
|
||||
key=".0:$.0"
|
||||
src=""
|
||||
width={16}
|
||||
>
|
||||
<ImageBase
|
||||
alt="Add Property"
|
||||
alt="Add Entity"
|
||||
height={30}
|
||||
src=""
|
||||
styles={[Function]}
|
||||
@@ -337,7 +335,7 @@ exports[`Excute Add Table Entity Pane should render Default properly 1`] = `
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="Add Property"
|
||||
alt="Add Entity"
|
||||
className="ms-Image-image ms-Image-image--portrait is-notLoaded is-fadeIn image-55"
|
||||
key="fabricImage"
|
||||
onError={[Function]}
|
||||
|
||||
@@ -39,7 +39,6 @@ import SplitterLayout from "react-splitter-layout";
|
||||
import CopilotIcon from "../../../images/Copilot.svg";
|
||||
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
|
||||
import SaveQueryIcon from "../../../images/save-cosmos.svg";
|
||||
import { useTabs } from "../../hooks/useTabs";
|
||||
|
||||
interface QueryCopilotTabProps {
|
||||
initialInput: string;
|
||||
@@ -74,7 +73,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
const generateSQLQuery = async (): Promise<void> => {
|
||||
try {
|
||||
setIsGeneratingQuery(true);
|
||||
useTabs.getState().setIsTabExecuting(true);
|
||||
const payload = {
|
||||
containerSchema: QueryCopilotSampleContainerSchema,
|
||||
userPrompt: userInput,
|
||||
@@ -103,7 +101,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
throw error;
|
||||
} finally {
|
||||
setIsGeneratingQuery(false);
|
||||
useTabs.getState().setIsTabExecuting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -122,7 +119,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
const queryDocumentsPerPage = async (firstItemIndex: number, queryIterator: MinimalQueryIterator): Promise<void> => {
|
||||
try {
|
||||
setIsExecuting(true);
|
||||
useTabs.getState().setIsTabExecuting(true);
|
||||
const queryResults: QueryResults = await queryPagesUntilContentPresent(
|
||||
firstItemIndex,
|
||||
async (firstItemIndex: number) =>
|
||||
@@ -137,7 +133,6 @@ export const QueryCopilotTab: React.FC<QueryCopilotTabProps> = ({
|
||||
handleError(errorMessage, "executeQueryCopilotTab");
|
||||
} finally {
|
||||
setIsExecuting(false);
|
||||
useTabs.getState().setIsTabExecuting(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { DefaultButton, IconButton, Image, Modal, PrimaryButton, Stack, Text } from "@fluentui/react";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceSuccess } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import { useCarousel } from "hooks/useCarousel";
|
||||
import React, { useState } from "react";
|
||||
import Youtube from "react-youtube";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceSuccess } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "UserContext";
|
||||
import Image1 from "../../../images/CarouselImage1.svg";
|
||||
import Image2 from "../../../images/CarouselImage2.svg";
|
||||
|
||||
@@ -25,7 +25,7 @@ export const QuickstartCarousel: React.FC<QuickstartCarouselProps> = ({
|
||||
<Stack>
|
||||
<Stack horizontal horizontalAlign="space-between" style={{ padding: 16 }}>
|
||||
<Text variant="xLarge">{getHeaderText(page)}</Text>
|
||||
<IconButton iconProps={{ iconName: "Cancel" }} onClick={() => setPage(4)} ariaLabel="Close" />
|
||||
<IconButton iconProps={{ iconName: "Cancel" }} onClick={() => setPage(4)} />
|
||||
</Stack>
|
||||
{getContent(page)}
|
||||
<Text variant="medium" style={{ padding: "0 16px" }}>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { extractPartitionKey, ItemDefinition, PartitionKeyDefinition, QueryIterator, Resource } from "@azure/cosmos";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import * as ko from "knockout";
|
||||
import Q from "q";
|
||||
import CopilotQueryTablesTab from "../../../images/CopilotQueryTablesTab.svg";
|
||||
import DeleteDocumentIcon from "../../../images/DeleteDocument.svg";
|
||||
import DiscardIcon from "../../../images/discard.svg";
|
||||
import NewDocumentIcon from "../../../images/NewDocument.svg";
|
||||
@@ -304,6 +306,7 @@ export default class DocumentsTab extends TabsBase {
|
||||
return true;
|
||||
}),
|
||||
};
|
||||
|
||||
this.buildCommandBarOptions();
|
||||
this.shouldShowEditor = ko.computed<boolean>(() => {
|
||||
const documentHasContent: boolean =
|
||||
@@ -768,6 +771,10 @@ export default class DocumentsTab extends TabsBase {
|
||||
}
|
||||
};
|
||||
|
||||
public onCopilotEntityClick = (): void => {
|
||||
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
||||
};
|
||||
|
||||
protected _loadNextPageInternal(): Q.Promise<DataModels.DocumentId[]> {
|
||||
return Q(this._documentsIterator.fetchNext().then((response) => response.resources));
|
||||
}
|
||||
@@ -884,6 +891,19 @@ export default class DocumentsTab extends TabsBase {
|
||||
buttons.push(DocumentsTab._createUploadButton(this.collection.container));
|
||||
}
|
||||
|
||||
if (userContext.features.enableCopilot) {
|
||||
const label = "Query Copliot";
|
||||
buttons.push({
|
||||
iconSrc: CopilotQueryTablesTab,
|
||||
iconAlt: label,
|
||||
onCommandClick: this.onCopilotEntityClick,
|
||||
commandButtonLabel: label,
|
||||
ariaLabel: label,
|
||||
hasPopup: true,
|
||||
disabled: false,
|
||||
});
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
|
||||
firstItemIndex,
|
||||
queryDocuments
|
||||
);
|
||||
this.setState({ queryResults, error: "" });
|
||||
this.setState({ queryResults });
|
||||
} catch (error) {
|
||||
this.props.tabsBaseInstance.isExecutionError(true);
|
||||
this.setState({
|
||||
|
||||
@@ -77,13 +77,7 @@
|
||||
<div class="addClause" data-bind=" ">
|
||||
<div class="addClause-heading">
|
||||
<span class="clause-table addClause-title">
|
||||
<img
|
||||
class="addclauseProperty-Img"
|
||||
style="margin-bottom: 5px"
|
||||
src="/Add-property.svg"
|
||||
alt="Add new Clause"
|
||||
role="presentation"
|
||||
/>
|
||||
<img class="addclauseProperty-Img" style="margin-bottom: 5px" src="/Add-property.svg" />
|
||||
<span style="margin-left: 5px" data-bind="text: addNewClauseLine"></span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -125,12 +119,7 @@
|
||||
aria-label="Show top results"
|
||||
/>
|
||||
<div role="alert" aria-atomic="true" class="inline-div" data-bind="visible: isExceedingLimit">
|
||||
<img
|
||||
class="advanced-options-icon"
|
||||
src="/QueryBuilder/StatusWarning_16x.png"
|
||||
alt="Warning"
|
||||
role="presentation"
|
||||
/>
|
||||
<img class="advanced-options-icon" src="/QueryBuilder/StatusWarning_16x.png" />
|
||||
<span data-bind="text: topValueLimitMessage"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -113,7 +113,7 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
|
||||
<div className="tab_Content">
|
||||
<span className="statusIconContainer" style={{ width: tabKind === ReactTabKind.Home ? 0 : 18 }}>
|
||||
{useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />}
|
||||
{isTabExecuting(tab, tabKind) && (
|
||||
{useObservable(tab?.isExecuting || ko.observable(false)) && (
|
||||
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" />
|
||||
)}
|
||||
</span>
|
||||
@@ -211,15 +211,6 @@ const onKeyPressReactTab = (e: KeyboardEvent, tabKind: ReactTabKind): void => {
|
||||
}
|
||||
};
|
||||
|
||||
const isTabExecuting = (tab?: Tab, tabKind?: ReactTabKind): boolean => {
|
||||
if (useObservable(tab?.isExecuting || ko.observable(false))) {
|
||||
return true;
|
||||
} else if (tabKind !== undefined && tabKind !== ReactTabKind.Home && useTabs.getState()?.isTabExecuting) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
|
||||
switch (activeReactTab) {
|
||||
case ReactTabKind.Connect:
|
||||
|
||||
@@ -11,7 +11,6 @@ interface TabsState {
|
||||
activeReactTab: ReactTabKind | undefined;
|
||||
networkSettingsWarning: string;
|
||||
queryCopilotTabInitialInput: string;
|
||||
isTabExecuting: boolean;
|
||||
activateTab: (tab: TabsBase) => void;
|
||||
activateNewTab: (tab: TabsBase) => void;
|
||||
activateReactTab: (tabkind: ReactTabKind) => void;
|
||||
@@ -25,7 +24,6 @@ interface TabsState {
|
||||
closeReactTab: (tabKind: ReactTabKind) => void;
|
||||
setNetworkSettingsWarning: (warningMessage: string) => void;
|
||||
setQueryCopilotTabInitialInput: (input: string) => void;
|
||||
setIsTabExecuting: (state: boolean) => void;
|
||||
}
|
||||
|
||||
export enum ReactTabKind {
|
||||
@@ -42,7 +40,6 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||
activeReactTab: ReactTabKind.Home,
|
||||
networkSettingsWarning: "",
|
||||
queryCopilotTabInitialInput: "",
|
||||
isTabExecuting: false,
|
||||
activateTab: (tab: TabsBase): void => {
|
||||
if (get().openedTabs.some((openedTab) => openedTab.tabId === tab.tabId)) {
|
||||
set({ activeTab: tab, activeReactTab: undefined });
|
||||
@@ -154,7 +151,4 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
|
||||
},
|
||||
setNetworkSettingsWarning: (warningMessage: string) => set({ networkSettingsWarning: warningMessage }),
|
||||
setQueryCopilotTabInitialInput: (input: string) => set({ queryCopilotTabInitialInput: input }),
|
||||
setIsTabExecuting: (state: boolean) => {
|
||||
set({ isTabExecuting: state });
|
||||
},
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user