mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-04-18 12:29:48 +01:00
Localization second batch (#2410)
* Localization second batch * update test * fix tests * Fix test
This commit is contained in:
@@ -7,6 +7,8 @@ import {
|
||||
AddGlobalSecondaryIndexPanelProps,
|
||||
} from "Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { isFabric, isFabricNative, openRestoreContainerDialog } from "Platform/Fabric/FabricUtil";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||
@@ -24,6 +26,7 @@ import DeleteTriggerIcon from "../../images/DeleteTrigger.svg";
|
||||
import DeleteUDFIcon from "../../images/DeleteUDF.svg";
|
||||
import HostedTerminalIcon from "../../images/Hosted-Terminal.svg";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { extractFeatures } from "../Platform/Hosted/extractFeatures";
|
||||
import { userContext } from "../UserContext";
|
||||
import { getCollectionName, getDatabaseName } from "../Utils/APITypeUtils";
|
||||
import { useSidePanel } from "../hooks/useSidePanel";
|
||||
@@ -35,7 +38,6 @@ import StoredProcedure from "./Tree/StoredProcedure";
|
||||
import Trigger from "./Tree/Trigger";
|
||||
import UserDefinedFunction from "./Tree/UserDefinedFunction";
|
||||
import { useSelectedNode } from "./useSelectedNode";
|
||||
import { extractFeatures } from "../Platform/Hosted/extractFeatures";
|
||||
|
||||
export interface CollectionContextMenuButtonParams {
|
||||
databaseId: string;
|
||||
@@ -57,7 +59,7 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
|
||||
{
|
||||
iconSrc: AddCollectionIcon,
|
||||
onClick: () => container.onNewCollectionClicked({ databaseId }),
|
||||
label: `New ${getCollectionName()}`,
|
||||
label: t(Keys.contextMenu.newContainer, { containerName: getCollectionName() }),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -67,7 +69,7 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
|
||||
items.push({
|
||||
iconSrc: AddCollectionIcon,
|
||||
onClick: () => openRestoreContainerDialog(),
|
||||
label: `Restore ${getCollectionName()}`,
|
||||
label: t(Keys.contextMenu.restoreContainer, { containerName: getCollectionName() }),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -80,11 +82,11 @@ export const createDatabaseContextMenu = (container: Explorer, databaseId: strin
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Delete " + getDatabaseName(),
|
||||
t(Keys.contextMenu.deleteDatabase, { databaseName: getDatabaseName() }),
|
||||
<DeleteDatabaseConfirmationPanel refreshDatabases={() => container.refreshAllDatabases()} />,
|
||||
);
|
||||
},
|
||||
label: `Delete ${getDatabaseName()}`,
|
||||
label: t(Keys.contextMenu.deleteDatabase, { databaseName: getDatabaseName() }),
|
||||
styleClass: "deleteDatabaseMenuItem",
|
||||
});
|
||||
}
|
||||
@@ -100,7 +102,7 @@ export const createCollectionContextMenuButton = (
|
||||
items.push({
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
onClick: () => selectedCollection && selectedCollection.onNewQueryClick(selectedCollection, undefined),
|
||||
label: "New SQL Query",
|
||||
label: t(Keys.contextMenu.newSqlQuery),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,7 +110,7 @@ export const createCollectionContextMenuButton = (
|
||||
items.push({
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
onClick: () => selectedCollection && selectedCollection.onNewMongoQueryClick(selectedCollection, undefined),
|
||||
label: "New Query",
|
||||
label: t(Keys.contextMenu.newQuery),
|
||||
});
|
||||
|
||||
items.push({
|
||||
@@ -123,8 +125,8 @@ export const createCollectionContextMenuButton = (
|
||||
},
|
||||
label:
|
||||
useNotebook.getState().isShellEnabled || userContext.features.enableCloudShell
|
||||
? "Open Mongo Shell"
|
||||
: "New Shell",
|
||||
? t(Keys.contextMenu.openMongoShell)
|
||||
: t(Keys.contextMenu.newShell),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -137,7 +139,7 @@ export const createCollectionContextMenuButton = (
|
||||
onClick: () => {
|
||||
container.openNotebookTerminal(ViewModels.TerminalKind.Cassandra);
|
||||
},
|
||||
label: "Open Cassandra Shell",
|
||||
label: t(Keys.contextMenu.openCassandraShell),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -150,7 +152,7 @@ export const createCollectionContextMenuButton = (
|
||||
onClick: () => {
|
||||
selectedCollection && selectedCollection.onNewStoredProcedureClick(selectedCollection, undefined);
|
||||
},
|
||||
label: "New Stored Procedure",
|
||||
label: t(Keys.contextMenu.newStoredProcedure),
|
||||
});
|
||||
|
||||
items.push({
|
||||
@@ -158,7 +160,7 @@ export const createCollectionContextMenuButton = (
|
||||
onClick: () => {
|
||||
selectedCollection && selectedCollection.onNewUserDefinedFunctionClick(selectedCollection);
|
||||
},
|
||||
label: "New UDF",
|
||||
label: t(Keys.contextMenu.newUdf),
|
||||
});
|
||||
|
||||
items.push({
|
||||
@@ -166,7 +168,7 @@ export const createCollectionContextMenuButton = (
|
||||
onClick: () => {
|
||||
selectedCollection && selectedCollection.onNewTriggerClick(selectedCollection, undefined);
|
||||
},
|
||||
label: "New Trigger",
|
||||
label: t(Keys.contextMenu.newTrigger),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -179,11 +181,11 @@ export const createCollectionContextMenuButton = (
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Delete " + getCollectionName(),
|
||||
t(Keys.contextMenu.deleteContainer, { containerName: getCollectionName() }),
|
||||
<DeleteCollectionConfirmationPane refreshDatabases={() => container.refreshAllDatabases()} />,
|
||||
);
|
||||
},
|
||||
label: `Delete ${getCollectionName()}`,
|
||||
label: t(Keys.contextMenu.deleteContainer, { containerName: getCollectionName() }),
|
||||
styleClass: "deleteCollectionMenuItem",
|
||||
});
|
||||
}
|
||||
@@ -220,14 +222,14 @@ export const createSampleCollectionContextMenuButton = (): TreeNodeMenuItem[] =>
|
||||
useTabs.getState().openAndActivateReactTab(ReactTabKind.QueryCopilot);
|
||||
traceOpen(Action.OpenQueryCopilotFromNewQuery, { apiType: userContext.apiType });
|
||||
},
|
||||
label: "New SQL Query",
|
||||
label: t(Keys.contextMenu.newSqlQuery),
|
||||
});
|
||||
} else if (copilotVersion === "v2.0") {
|
||||
const sampleCollection = useDatabases.getState().sampleDataResourceTokenCollection;
|
||||
items.push({
|
||||
iconSrc: AddSqlQueryIcon,
|
||||
onClick: () => sampleCollection && sampleCollection.onNewQueryClick(sampleCollection, undefined),
|
||||
label: "New SQL Query",
|
||||
label: t(Keys.contextMenu.newSqlQuery),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -247,7 +249,7 @@ export const createStoreProcedureContextMenuItems = (
|
||||
{
|
||||
iconSrc: DeleteSprocIcon,
|
||||
onClick: () => storedProcedure.delete(),
|
||||
label: "Delete Stored Procedure",
|
||||
label: t(Keys.contextMenu.deleteStoredProcedure),
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -261,7 +263,7 @@ export const createTriggerContextMenuItems = (container: Explorer, trigger: Trig
|
||||
{
|
||||
iconSrc: DeleteTriggerIcon,
|
||||
onClick: () => trigger.delete(),
|
||||
label: "Delete Trigger",
|
||||
label: t(Keys.contextMenu.deleteTrigger),
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -278,7 +280,7 @@ export const createUserDefinedFunctionContextMenuItems = (
|
||||
{
|
||||
iconSrc: DeleteUDFIcon,
|
||||
onClick: () => userDefinedFunction.delete(),
|
||||
label: "Delete User Defined Function",
|
||||
label: t(Keys.contextMenu.deleteUdf),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -18,6 +18,8 @@ import { queryConflicts } from "../../Common/dataAccess/queryConflicts";
|
||||
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { Keys } from "../../Localization/Keys.generated";
|
||||
import { t } from "../../Localization/t";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||
@@ -57,7 +59,7 @@ export default class ConflictsTab extends TabsBase {
|
||||
|
||||
private _documentsIterator: MinimalQueryIterator;
|
||||
private _container: Explorer;
|
||||
private _acceptButtonLabel: ko.Observable<string> = ko.observable("Save");
|
||||
private _acceptButtonLabel: ko.Observable<string> = ko.observable(t(Keys.common.save));
|
||||
|
||||
constructor(options: ViewModels.ConflictsTabOptions) {
|
||||
super(options);
|
||||
@@ -213,9 +215,9 @@ export default class ConflictsTab extends TabsBase {
|
||||
this.selectedConflictContent.subscribe((newContent: string) => this._onEditorContentChange(newContent));
|
||||
|
||||
this.conflictOperation.subscribe((newOperationType: string) => {
|
||||
let operationLabel = "Save";
|
||||
let operationLabel = t(Keys.common.save);
|
||||
if (newOperationType === Constants.ConflictOperationType.Replace) {
|
||||
operationLabel = "Update";
|
||||
operationLabel = t(Keys.common.update);
|
||||
}
|
||||
|
||||
this._acceptButtonLabel(operationLabel);
|
||||
@@ -229,7 +231,7 @@ export default class ConflictsTab extends TabsBase {
|
||||
this._documentsIterator = this.createIterator();
|
||||
await this.loadNextPage();
|
||||
} catch (error) {
|
||||
useDialog.getState().showOkModalDialog("Refresh documents grid failed", getErrorMessage(error));
|
||||
useDialog.getState().showOkModalDialog(t(Keys.tabs.conflicts.refreshGridFailed), getErrorMessage(error));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,11 +259,11 @@ export default class ConflictsTab extends TabsBase {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkCancelModalDialog(
|
||||
"Unsaved changes",
|
||||
"Changes will be lost. Do you want to continue?",
|
||||
"OK",
|
||||
t(Keys.tabs.conflicts.unsavedChanges),
|
||||
t(Keys.tabs.conflicts.changesWillBeLost),
|
||||
t(Keys.common.ok),
|
||||
async () => await this.resolveConflict(),
|
||||
"Cancel",
|
||||
t(Keys.common.cancel),
|
||||
undefined,
|
||||
);
|
||||
} else {
|
||||
@@ -332,7 +334,7 @@ export default class ConflictsTab extends TabsBase {
|
||||
} catch (error) {
|
||||
this.isExecutionError(true);
|
||||
const errorMessage = getErrorMessage(error);
|
||||
useDialog.getState().showOkModalDialog("Resolve conflict failed", errorMessage);
|
||||
useDialog.getState().showOkModalDialog(t(Keys.tabs.conflicts.resolveConflictFailed), errorMessage);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.ResolveConflict,
|
||||
{
|
||||
@@ -386,7 +388,7 @@ export default class ConflictsTab extends TabsBase {
|
||||
} catch (error) {
|
||||
this.isExecutionError(true);
|
||||
const errorMessage = getErrorMessage(error);
|
||||
useDialog.getState().showOkModalDialog("Delete conflict failed", errorMessage);
|
||||
useDialog.getState().showOkModalDialog(t(Keys.tabs.conflicts.deleteConflictFailed), errorMessage);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.DeleteConflict,
|
||||
{
|
||||
@@ -617,7 +619,7 @@ export default class ConflictsTab extends TabsBase {
|
||||
}
|
||||
|
||||
if (this.discardButton.visible()) {
|
||||
const label = "Discard";
|
||||
const label = t(Keys.common.discard);
|
||||
buttons.push({
|
||||
iconSrc: DiscardIcon,
|
||||
iconAlt: label,
|
||||
@@ -630,7 +632,7 @@ export default class ConflictsTab extends TabsBase {
|
||||
}
|
||||
|
||||
if (this.deleteButton.visible()) {
|
||||
const label = "Delete";
|
||||
const label = t(Keys.common.delete);
|
||||
buttons.push({
|
||||
iconSrc: DeleteIcon,
|
||||
iconAlt: label,
|
||||
|
||||
@@ -41,6 +41,8 @@ import { usePrevious } from "Explorer/Tabs/DocumentsTabV2/SelectionHelper";
|
||||
import { CosmosFluentProvider, LayoutConstants, cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil";
|
||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||
import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { isFabric } from "Platform/Fabric/FabricUtil";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
@@ -349,7 +351,7 @@ export const getTabsButtons = ({
|
||||
}
|
||||
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
const label = !isPreferredApiMongoDB ? "New Item" : "New Document";
|
||||
const label = !isPreferredApiMongoDB ? t(Keys.tabs.documents.newItem) : t(Keys.tabs.documents.newDocument);
|
||||
if (getNewDocumentButtonState(editorState).visible) {
|
||||
buttons.push({
|
||||
iconSrc: NewDocumentIcon,
|
||||
@@ -368,7 +370,7 @@ export const getTabsButtons = ({
|
||||
}
|
||||
|
||||
if (getSaveNewDocumentButtonState(editorState).visible) {
|
||||
const label = "Save";
|
||||
const label = t(Keys.common.save);
|
||||
buttons.push({
|
||||
iconSrc: SaveIcon,
|
||||
iconAlt: label,
|
||||
@@ -386,7 +388,7 @@ export const getTabsButtons = ({
|
||||
}
|
||||
|
||||
if (getDiscardNewDocumentChangesButtonState(editorState).visible) {
|
||||
const label = "Discard";
|
||||
const label = t(Keys.common.discard);
|
||||
buttons.push({
|
||||
iconSrc: DiscardIcon,
|
||||
iconAlt: label,
|
||||
@@ -403,7 +405,7 @@ export const getTabsButtons = ({
|
||||
}
|
||||
|
||||
if (getSaveExistingDocumentButtonState(editorState).visible) {
|
||||
const label = "Update";
|
||||
const label = t(Keys.common.update);
|
||||
buttons.push({
|
||||
iconSrc: SaveIcon,
|
||||
iconAlt: label,
|
||||
@@ -421,7 +423,7 @@ export const getTabsButtons = ({
|
||||
}
|
||||
|
||||
if (getDiscardExistingDocumentChangesButtonState(editorState).visible) {
|
||||
const label = "Discard";
|
||||
const label = t(Keys.common.discard);
|
||||
buttons.push({
|
||||
iconSrc: DiscardIcon,
|
||||
iconAlt: label,
|
||||
@@ -438,7 +440,7 @@ export const getTabsButtons = ({
|
||||
}
|
||||
|
||||
if (selectedRows.size > 0) {
|
||||
const label = "Delete";
|
||||
const label = t(Keys.common.delete);
|
||||
buttons.push({
|
||||
iconSrc: DeleteDocumentIcon,
|
||||
iconAlt: label,
|
||||
@@ -453,7 +455,7 @@ export const getTabsButtons = ({
|
||||
}
|
||||
|
||||
if (!isPreferredApiMongoDB) {
|
||||
const label = "Upload Item";
|
||||
const label = t(Keys.tabs.documents.uploadItem);
|
||||
buttons.push({
|
||||
id: UPLOAD_BUTTON_ID,
|
||||
iconSrc: UploadIcon,
|
||||
@@ -737,17 +739,18 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
} else if (result.statusCode >= 400) {
|
||||
newFailed.push(result.documentId);
|
||||
logConsoleError(
|
||||
`Failed to delete document ${result.documentId.id()} with status code ${result.statusCode}`,
|
||||
t(Keys.tabs.documents.deleteDocumentFailedLog, {
|
||||
documentId: result.documentId.id(),
|
||||
statusCode: result.statusCode,
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
logConsoleInfo(`Successfully deleted ${newSuccessful.length} document(s)`);
|
||||
logConsoleInfo(t(Keys.tabs.documents.deleteSuccessLog, { count: newSuccessful.length }));
|
||||
|
||||
if (newThrottled.length > 0) {
|
||||
logConsoleError(
|
||||
`Failed to delete ${newThrottled.length} document(s) due to "Request too large" (429) error. Retrying...`,
|
||||
);
|
||||
logConsoleError(t(Keys.tabs.documents.deleteThrottledLog, { count: newThrottled.length }));
|
||||
}
|
||||
|
||||
// Update result of the bulk delete: method is called again, because the state variables changed
|
||||
@@ -917,11 +920,11 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkCancelModalDialog(
|
||||
"Unsaved changes",
|
||||
"Your unsaved changes will be lost. Do you want to continue?",
|
||||
"OK",
|
||||
t(Keys.tabs.documents.unsavedChanges),
|
||||
t(Keys.tabs.documents.unsavedChangesMessage),
|
||||
t(Keys.common.ok),
|
||||
onDiscard,
|
||||
"Cancel",
|
||||
t(Keys.common.cancel),
|
||||
onCancelDiscard,
|
||||
);
|
||||
} else {
|
||||
@@ -1011,7 +1014,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
(error) => {
|
||||
onExecutionErrorChange(true);
|
||||
const errorMessage = getErrorMessage(error);
|
||||
useDialog.getState().showOkModalDialog("Create document failed", errorMessage);
|
||||
useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.createDocumentFailed), errorMessage);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.CreateDocument,
|
||||
{
|
||||
@@ -1097,7 +1100,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
selectedDocumentId.partitionKeyValue = originalPartitionKeyValue;
|
||||
onExecutionErrorChange(true);
|
||||
const errorMessage = getErrorMessage(error);
|
||||
useDialog.getState().showOkModalDialog("Update document failed", errorMessage);
|
||||
useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.updateDocumentFailed), errorMessage);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.UpdateDocument,
|
||||
{
|
||||
@@ -1174,7 +1177,12 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
// Remove the check for systemKey, remove call to deleteNoSqlDocument(). deleteNoSqlDocuments() should
|
||||
// always be called for NoSQL.
|
||||
deletePromise = deleteNoSqlDocument(_collection, toDeleteDocumentIds[0]).then(() => {
|
||||
useDialog.getState().showOkModalDialog("Delete document", "Document successfully deleted.");
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog(
|
||||
t(Keys.tabs.documents.deleteDocumentDialogTitle),
|
||||
t(Keys.tabs.documents.documentDeleted),
|
||||
);
|
||||
return [toDeleteDocumentIds[0]];
|
||||
});
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
@@ -1251,17 +1259,20 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog(
|
||||
"Delete documents",
|
||||
`Some documents failed to delete due to a rate limiting error. Please try again later. To prevent this in the future, consider increasing the throughput on your container or database.`,
|
||||
t(Keys.tabs.documents.deleteDocumentsDialogTitle),
|
||||
t(Keys.tabs.documents.throttlingError),
|
||||
{
|
||||
linkText: "Learn More",
|
||||
linkText: t(Keys.common.learnMore),
|
||||
linkUrl: MONGO_THROTTLING_DOC_URL,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkModalDialog("Delete documents", `Deleting document(s) failed (${error.message})`);
|
||||
.showOkModalDialog(
|
||||
t(Keys.tabs.documents.deleteDocumentsDialogTitle),
|
||||
t(Keys.tabs.documents.deleteFailed, { error: error.message }),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -1275,21 +1286,21 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
const isPlural = selectedRows.size > 1;
|
||||
const documentName = !isPreferredApiMongoDB
|
||||
? isPlural
|
||||
? `the selected ${selectedRows.size} items`
|
||||
: "the selected item"
|
||||
? t(Keys.tabs.documents.selectedItems, { count: selectedRows.size })
|
||||
: t(Keys.tabs.documents.selectedItem)
|
||||
: isPlural
|
||||
? `the selected ${selectedRows.size} documents`
|
||||
: "the selected document";
|
||||
const msg = `Are you sure you want to delete ${documentName}?`;
|
||||
? t(Keys.tabs.documents.selectedDocuments, { count: selectedRows.size })
|
||||
: t(Keys.tabs.documents.selectedDocument);
|
||||
const msg = t(Keys.tabs.documents.confirmDelete, { documentName });
|
||||
|
||||
useDialog
|
||||
.getState()
|
||||
.showOkCancelModalDialog(
|
||||
"Confirm delete",
|
||||
t(Keys.tabs.documents.confirmDeleteTitle),
|
||||
msg,
|
||||
"Delete",
|
||||
t(Keys.common.delete),
|
||||
() => deleteDocuments(Array.from(selectedRows).map((index) => documentIds[index as number])),
|
||||
"Cancel",
|
||||
t(Keys.common.cancel),
|
||||
undefined,
|
||||
);
|
||||
}, [deleteDocuments, documentIds, isPreferredApiMongoDB, selectedRows]);
|
||||
@@ -1823,8 +1834,8 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
|
||||
const partitionKeyProperty = partitionKeyProperties?.[0];
|
||||
if (partitionKeyProperty !== "_id" && !_hasShardKeySpecified(documentContent)) {
|
||||
const message = `The document is lacking the shard property: ${partitionKeyProperty}`;
|
||||
useDialog.getState().showOkModalDialog("Create document failed", message);
|
||||
const message = t(Keys.tabs.documents.missingShardProperty, { partitionKeyProperty });
|
||||
useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.createDocumentFailed), message);
|
||||
onExecutionErrorChange(true);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.CreateDocument,
|
||||
@@ -1835,7 +1846,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
},
|
||||
startKey,
|
||||
);
|
||||
Logger.logError("Failed to save new document: Document shard key not defined", "MongoDocumentsTab");
|
||||
Logger.logError(t(Keys.tabs.documents.missingShardKeyLog), "MongoDocumentsTab");
|
||||
throw new Error("Document without shard key");
|
||||
}
|
||||
|
||||
@@ -1878,7 +1889,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
(error) => {
|
||||
onExecutionErrorChange(true);
|
||||
const errorMessage = getErrorMessage(error);
|
||||
useDialog.getState().showOkModalDialog("Create document failed", errorMessage);
|
||||
useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.createDocumentFailed), errorMessage);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.CreateDocument,
|
||||
{
|
||||
@@ -1949,7 +1960,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
(error) => {
|
||||
onExecutionErrorChange(true);
|
||||
const errorMessage = getErrorMessage(error);
|
||||
useDialog.getState().showOkModalDialog("Update document failed", errorMessage);
|
||||
useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.updateDocumentFailed), errorMessage);
|
||||
TelemetryProcessor.traceFailure(
|
||||
Action.UpdateDocument,
|
||||
{
|
||||
@@ -2058,7 +2069,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
useDialog.getState().showOkModalDialog("Refresh documents grid failed", getErrorMessage(error));
|
||||
useDialog.getState().showOkModalDialog(t(Keys.tabs.documents.refreshGridFailed), getErrorMessage(error));
|
||||
}
|
||||
},
|
||||
[createIterator, filterContent],
|
||||
@@ -2070,18 +2081,17 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
* @returns 429 warning message
|
||||
*/
|
||||
const get429WarningMessageNoSql = (): string => {
|
||||
let message = 'Some delete requests failed due to a "Request too large" exception (429)';
|
||||
let message = t(Keys.tabs.documents.requestTooLargeBase);
|
||||
|
||||
if (bulkDeleteOperation.count === bulkDeleteProcess.successfulIds.length) {
|
||||
message += ", but were successfully retried.";
|
||||
message += ", " + t(Keys.tabs.documents.retriedSuccessfully);
|
||||
} else if (bulkDeleteMode === "inProgress" || bulkDeleteMode === "aborting") {
|
||||
message += ". Retrying now.";
|
||||
message += ". " + t(Keys.tabs.documents.retryingNow);
|
||||
} else {
|
||||
message += ".";
|
||||
}
|
||||
|
||||
return (message +=
|
||||
" To prevent this in the future, consider increasing the throughput on your container or database.");
|
||||
return (message += " " + t(Keys.tabs.documents.increaseThroughputTip));
|
||||
};
|
||||
|
||||
const onColumnSelectionChange = (newSelectedColumnIds: string[]): void => {
|
||||
@@ -2128,7 +2138,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
const nonBlankLastFilters = lastFilterContents.filter((filter) => filter.trim() !== "");
|
||||
if (nonBlankLastFilters.length > 0) {
|
||||
options.push({
|
||||
label: "Saved filters",
|
||||
label: t(Keys.tabs.documents.savedFilters),
|
||||
options: nonBlankLastFilters,
|
||||
});
|
||||
}
|
||||
@@ -2157,14 +2167,14 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
dropdownOptions={getFilterChoices()}
|
||||
placeholder={
|
||||
isPreferredApiMongoDB
|
||||
? "Type a query predicate (e.g., {´a´:´foo´}), or choose one from the drop down list, or leave empty to query all documents."
|
||||
: "Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents."
|
||||
? t(Keys.tabs.documents.mongoFilterPlaceholder)
|
||||
: t(Keys.tabs.documents.sqlFilterPlaceholder)
|
||||
}
|
||||
title="Type a query predicate or choose one from the list."
|
||||
title={t(Keys.tabs.documents.filterTooltip)}
|
||||
value={filterContent}
|
||||
onChange={updateFilterContent}
|
||||
onKeyDown={onFilterKeyDown}
|
||||
bottomLink={{ text: "Learn more", url: DATA_EXPLORER_DOC_URL }}
|
||||
bottomLink={{ text: t(Keys.common.learnMore), url: DATA_EXPLORER_DOC_URL }}
|
||||
/>
|
||||
<Button
|
||||
appearance="primary"
|
||||
@@ -2180,10 +2190,12 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
}
|
||||
}}
|
||||
disabled={isExecuting && isPreferredApiMongoDB}
|
||||
aria-label={!isExecuting || isPreferredApiMongoDB ? "Apply filter" : "Cancel"}
|
||||
aria-label={
|
||||
!isExecuting || isPreferredApiMongoDB ? t(Keys.tabs.documents.applyFilter) : t(Keys.common.cancel)
|
||||
}
|
||||
tabIndex={0}
|
||||
>
|
||||
{!isExecuting || isPreferredApiMongoDB ? "Apply Filter" : "Cancel"}
|
||||
{!isExecuting || isPreferredApiMongoDB ? t(Keys.tabs.documents.applyFilter) : t(Keys.common.cancel)}
|
||||
</Button>
|
||||
</div>
|
||||
<Allotment
|
||||
@@ -2227,14 +2239,14 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
</div>
|
||||
{tableContainerSizePx?.width >= calculateOffset(selectedColumnIds.length) + 200 && (
|
||||
<div
|
||||
title="Refresh"
|
||||
title={t(Keys.common.refresh)}
|
||||
className={styles.refreshBtn}
|
||||
role="button"
|
||||
onClick={() => refreshDocumentsGrid(false)}
|
||||
aria-label="Refresh"
|
||||
aria-label={t(Keys.common.refresh)}
|
||||
tabIndex={0}
|
||||
>
|
||||
<img src={RefreshIcon} alt="Refresh" />
|
||||
<img src={RefreshIcon} alt={t(Keys.common.refresh)} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -2247,7 +2259,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
onClick={() => loadNextPage(documentsIterator.iterator, false)}
|
||||
onKeyDown={onLoadMoreKeyInput}
|
||||
>
|
||||
Load more
|
||||
{t(Keys.tabs.documents.loadMore)}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
@@ -2259,7 +2271,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
language={"json"}
|
||||
content={selectedDocumentContent}
|
||||
isReadOnly={false}
|
||||
ariaLabel={"Document editor"}
|
||||
ariaLabel={t(Keys.tabs.documents.documentEditor)}
|
||||
lineNumbers={"on"}
|
||||
theme={"_theme"}
|
||||
onContentChanged={_onEditorContentChange}
|
||||
@@ -2267,7 +2279,9 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
/>
|
||||
)}
|
||||
{selectedRows.size > 1 && (
|
||||
<span style={{ margin: 10 }}>Number of selected documents: {selectedRows.size}</span>
|
||||
<span style={{ margin: 10 }}>
|
||||
{t(Keys.tabs.documents.numberOfSelectedDocuments, { count: selectedRows.size })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Allotment.Pane>
|
||||
@@ -2276,42 +2290,43 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
||||
{bulkDeleteOperation && (
|
||||
<ProgressModalDialog
|
||||
isOpen={isBulkDeleteDialogOpen}
|
||||
dismissText="Abort"
|
||||
dismissText={t(Keys.tabs.documents.abort)}
|
||||
onDismiss={() => {
|
||||
setIsBulkDeleteDialogOpen(false);
|
||||
setBulkDeleteOperation(undefined);
|
||||
}}
|
||||
onCancel={() => setBulkDeleteMode("aborting")}
|
||||
title={`Deleting ${bulkDeleteOperation.count} document(s)`}
|
||||
message={`Successfully deleted ${bulkDeleteProcess.successfulIds.length} document(s).`}
|
||||
title={t(Keys.tabs.documents.deletingDocuments, { count: bulkDeleteOperation.count })}
|
||||
message={t(Keys.tabs.documents.deletedDocumentsSuccess, { count: bulkDeleteProcess.successfulIds.length })}
|
||||
maxValue={bulkDeleteOperation.count}
|
||||
value={bulkDeleteProcess.successfulIds.length}
|
||||
mode={bulkDeleteMode}
|
||||
>
|
||||
<div className={styles.deleteProgressContent}>
|
||||
{(bulkDeleteMode === "aborting" || bulkDeleteMode === "aborted") && (
|
||||
<div style={{ paddingBottom: tokens.spacingVerticalL }}>Deleting document(s) was aborted.</div>
|
||||
<div style={{ paddingBottom: tokens.spacingVerticalL }}>{t(Keys.tabs.documents.deleteAborted)}</div>
|
||||
)}
|
||||
{(bulkDeleteProcess.failedIds.length > 0 ||
|
||||
(bulkDeleteProcess.throttledIds.length > 0 && bulkDeleteMode !== "inProgress")) && (
|
||||
<MessageBar intent="error" style={{ marginBottom: tokens.spacingVerticalL }}>
|
||||
<MessageBarBody>
|
||||
<MessageBarTitle>Error</MessageBarTitle>
|
||||
Failed to delete{" "}
|
||||
{bulkDeleteMode === "inProgress"
|
||||
<MessageBarTitle>{t(Keys.tabs.documents.error)}</MessageBarTitle>
|
||||
{t(Keys.tabs.documents.failedToDeleteDocuments, {
|
||||
count:
|
||||
bulkDeleteMode === "inProgress"
|
||||
? bulkDeleteProcess.failedIds.length
|
||||
: bulkDeleteProcess.failedIds.length + bulkDeleteProcess.throttledIds.length}{" "}
|
||||
document(s).
|
||||
: bulkDeleteProcess.failedIds.length + bulkDeleteProcess.throttledIds.length,
|
||||
})}
|
||||
</MessageBarBody>
|
||||
</MessageBar>
|
||||
)}
|
||||
{bulkDeleteProcess.hasBeenThrottled && (
|
||||
<MessageBar intent="warning">
|
||||
<MessageBarBody>
|
||||
<MessageBarTitle>Warning</MessageBarTitle>
|
||||
<MessageBarTitle>{t(Keys.tabs.documents.warning)}</MessageBarTitle>
|
||||
{get429WarningMessageNoSql()}{" "}
|
||||
<Link href={NO_SQL_THROTTLING_DOC_URL} target="_blank">
|
||||
Learn More
|
||||
{t(Keys.common.learnMore)}
|
||||
</Link>
|
||||
</MessageBarBody>
|
||||
</MessageBar>
|
||||
|
||||
@@ -44,13 +44,13 @@ exports[`Documents tab (noSql API) when rendered should render the page 1`] = `
|
||||
}
|
||||
onChange={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
placeholder="Type a query predicate (e.g., WHERE c.id=´1´), or choose one from the drop down list, or leave empty to query all documents."
|
||||
placeholder="Type a query predicate (e.g., WHERE c.id="1"), or choose one from the drop down list, or leave empty to query all documents."
|
||||
title="Type a query predicate or choose one from the list."
|
||||
value=""
|
||||
/>
|
||||
<Button
|
||||
appearance="primary"
|
||||
aria-label="Apply filter"
|
||||
aria-label="Apply Filter"
|
||||
data-test="DocumentsTab/ApplyFilter"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { Component } from "react";
|
||||
import { configContext } from "../../../ConfigContext";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../../UserContext";
|
||||
@@ -212,7 +214,7 @@ export default class MongoShellTabComponent extends Component<
|
||||
src={this.state.url}
|
||||
id={this.props.tabsBaseInstance.tabId}
|
||||
onLoad={(event) => this.setContentFocus(event)}
|
||||
title="Mongo Shell"
|
||||
title={t(Keys.tabs.mongoShell.title)}
|
||||
role="tabpanel"
|
||||
></iframe>
|
||||
);
|
||||
|
||||
@@ -17,6 +17,8 @@ import { QueryTabStyles, useQueryTabStyles } from "Explorer/Tabs/QueryTab/Styles
|
||||
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
|
||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||
import { KeyboardAction } from "KeyboardShortcuts";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
@@ -315,7 +317,9 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
||||
};
|
||||
|
||||
public onSaveQueryClick = (): void => {
|
||||
useSidePanel.getState().openSidePanel("Save Query", <SaveQueryPane explorer={this.props.collection.container} />);
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(t(Keys.tabs.query.saveQuery), <SaveQueryPane explorer={this.props.collection.container} />);
|
||||
};
|
||||
|
||||
public launchQueryCopilotChat = (): void => {
|
||||
@@ -325,7 +329,10 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
||||
public onSavedQueriesClick = (): void => {
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel("Open Saved Queries", <BrowseQueriesPane explorer={this.props.collection.container} />);
|
||||
.openSidePanel(
|
||||
t(Keys.tabs.query.openSavedQueries),
|
||||
<BrowseQueriesPane explorer={this.props.collection.container} />,
|
||||
);
|
||||
};
|
||||
|
||||
public toggleResult(): void {
|
||||
@@ -473,7 +480,8 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
||||
protected getTabsButtons(): CommandButtonComponentProps[] {
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
if (this.executeQueryButton.visible) {
|
||||
const label = this.state.selectedContent?.length > 0 ? "Execute Selection" : "Execute Query";
|
||||
const label =
|
||||
this.state.selectedContent?.length > 0 ? t(Keys.tabs.query.executeSelection) : t(Keys.tabs.query.executeQuery);
|
||||
buttons.push({
|
||||
iconSrc: ExecuteQueryIcon,
|
||||
iconAlt: label,
|
||||
@@ -490,7 +498,7 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
||||
|
||||
if (this.saveQueryButton.visible) {
|
||||
if (configContext.platform !== Platform.Fabric) {
|
||||
const label = "Save Query";
|
||||
const label = t(Keys.tabs.query.saveQuery);
|
||||
buttons.push({
|
||||
iconSrc: SaveQueryIcon,
|
||||
iconAlt: label,
|
||||
@@ -507,11 +515,11 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
||||
|
||||
buttons.push({
|
||||
iconSrc: DownloadQueryIcon,
|
||||
iconAlt: "Download Query",
|
||||
iconAlt: t(Keys.tabs.query.downloadQuery),
|
||||
keyboardAction: KeyboardAction.DOWNLOAD_ITEM,
|
||||
onCommandClick: this.onDownloadQueryClick,
|
||||
commandButtonLabel: "Download Query",
|
||||
ariaLabel: "Download Query",
|
||||
commandButtonLabel: t(Keys.tabs.query.downloadQuery),
|
||||
ariaLabel: t(Keys.tabs.query.downloadQuery),
|
||||
hasPopup: false,
|
||||
disabled: !this.saveQueryButton.enabled,
|
||||
});
|
||||
@@ -568,7 +576,7 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
||||
// }
|
||||
|
||||
if (!this.props.isPreferredApiMongoDB && this.state.isExecuting) {
|
||||
const label = "Cancel query";
|
||||
const label = t(Keys.tabs.query.cancelQuery);
|
||||
buttons.push({
|
||||
iconSrc: CancelQueryIcon,
|
||||
iconAlt: label,
|
||||
@@ -589,23 +597,23 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
||||
const verticalButton: CommandButtonComponentProps = {
|
||||
isSelected: this.state.queryResultsView === SplitterDirection.Vertical,
|
||||
iconSrc: this.state.queryResultsView === SplitterDirection.Vertical ? CheckIcon : undefined,
|
||||
commandButtonLabel: "Vertical",
|
||||
ariaLabel: "Vertical",
|
||||
commandButtonLabel: t(Keys.tabs.query.vertical),
|
||||
ariaLabel: t(Keys.tabs.query.vertical),
|
||||
onCommandClick: () => this._setViewLayout(SplitterDirection.Vertical),
|
||||
hasPopup: false,
|
||||
};
|
||||
const horizontalButton: CommandButtonComponentProps = {
|
||||
isSelected: this.state.queryResultsView === SplitterDirection.Horizontal,
|
||||
iconSrc: this.state.queryResultsView === SplitterDirection.Horizontal ? CheckIcon : undefined,
|
||||
commandButtonLabel: "Horizontal",
|
||||
ariaLabel: "Horizontal",
|
||||
commandButtonLabel: t(Keys.tabs.query.horizontal),
|
||||
ariaLabel: t(Keys.tabs.query.horizontal),
|
||||
onCommandClick: () => this._setViewLayout(SplitterDirection.Horizontal),
|
||||
hasPopup: false,
|
||||
};
|
||||
|
||||
return {
|
||||
commandButtonLabel: "View",
|
||||
ariaLabel: "View",
|
||||
commandButtonLabel: t(Keys.tabs.query.view),
|
||||
ariaLabel: t(Keys.tabs.query.view),
|
||||
hasPopup: true,
|
||||
children: [verticalButton, horizontalButton],
|
||||
};
|
||||
@@ -782,7 +790,7 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
|
||||
modelMarkers={this.state.modelMarkers}
|
||||
isReadOnly={false}
|
||||
wordWrap={"on"}
|
||||
ariaLabel={"Editing Query"}
|
||||
ariaLabel={t(Keys.tabs.query.editingQuery)}
|
||||
lineNumbers={"on"}
|
||||
theme={this.props.monacoTheme}
|
||||
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}
|
||||
|
||||
@@ -11,6 +11,8 @@ import { createStoredProcedure } from "../../../Common/dataAccess/createStoredPr
|
||||
import { ExecuteSprocResult } from "../../../Common/dataAccess/executeStoredProcedure";
|
||||
import { updateStoredProcedure } from "../../../Common/dataAccess/updateStoredProcedure";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
|
||||
import { useTabs } from "../../../hooks/useTabs";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
@@ -133,7 +135,7 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
this.collection = this.props.collection;
|
||||
this.executeResultsEditorId = `executestoredprocedureresults${this.props.scriptTabBaseInstance.tabId}`;
|
||||
this.executeLogsEditorId = `executestoredprocedurelogs${this.props.scriptTabBaseInstance.tabId}`;
|
||||
this.props.scriptTabBaseInstance.ariaLabel("Stored Procedure Body");
|
||||
this.props.scriptTabBaseInstance.ariaLabel(t(Keys.tabs.storedProcedure.body));
|
||||
|
||||
this.props.iStorProcTabComponentAccessor({
|
||||
onExecuteSprocsResultEvent: this.onExecuteSprocsResult.bind(this),
|
||||
@@ -318,7 +320,7 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
|
||||
protected getTabsButtons(): CommandButtonComponentProps[] {
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
const label = "Save";
|
||||
const label = t(Keys.common.save);
|
||||
if (this.state.saveButton.visible) {
|
||||
buttons.push({
|
||||
iconSrc: SaveIcon,
|
||||
@@ -333,7 +335,7 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
}
|
||||
|
||||
if (this.state.updateButton.visible) {
|
||||
const label = "Update";
|
||||
const label = t(Keys.common.update);
|
||||
buttons.push({
|
||||
iconSrc: SaveIcon,
|
||||
iconAlt: label,
|
||||
@@ -347,7 +349,7 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
}
|
||||
|
||||
if (this.state.discardButton.visible) {
|
||||
const label = "Discard";
|
||||
const label = t(Keys.common.discard);
|
||||
buttons.push({
|
||||
iconSrc: DiscardIcon,
|
||||
iconAlt: label,
|
||||
@@ -361,7 +363,7 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
}
|
||||
|
||||
if (this.state.executeButton.visible) {
|
||||
const label = "Execute";
|
||||
const label = t(Keys.common.execute);
|
||||
buttons.push({
|
||||
iconSrc: ExecuteQueryIcon,
|
||||
iconAlt: label,
|
||||
@@ -519,7 +521,7 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
<div className="tab-pane flexContainer stored-procedure-tab" role="tabpanel">
|
||||
<div className="storedTabForm flexContainer">
|
||||
<div className="formTitleFirst">
|
||||
Stored Procedure Id
|
||||
{t(Keys.tabs.storedProcedure.id)}
|
||||
<span className="mandatoryStar" style={{ color: "#ff0707", fontSize: "14px", fontWeight: "bold" }}>
|
||||
*
|
||||
</span>
|
||||
@@ -531,28 +533,28 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
required
|
||||
pattern={ValidCosmosDbIdInputPattern.source}
|
||||
title={ValidCosmosDbIdDescription}
|
||||
aria-label="Stored procedure id"
|
||||
placeholder="Enter the new stored procedure id"
|
||||
aria-label={t(Keys.tabs.storedProcedure.idAriaLabel)}
|
||||
placeholder={t(Keys.tabs.storedProcedure.idPlaceholder)}
|
||||
size={40}
|
||||
value={this.state.id}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => this.handleIdOnChange(event)}
|
||||
/>
|
||||
</span>
|
||||
<div className="spUdfTriggerHeader">Stored Procedure Body</div>
|
||||
<div className="spUdfTriggerHeader">{t(Keys.tabs.storedProcedure.body)}</div>
|
||||
<EditorReact
|
||||
language={"javascript"}
|
||||
content={this.state.sProcEditorContent}
|
||||
isReadOnly={false}
|
||||
ariaLabel={"Stored procedure body"}
|
||||
ariaLabel={t(Keys.tabs.storedProcedure.bodyAriaLabel)}
|
||||
lineNumbers={"on"}
|
||||
theme={"_theme"}
|
||||
onContentChanged={(newContent: string) => this.onChangeContent(newContent)}
|
||||
/>
|
||||
{this.state.hasResults && (
|
||||
<div className="results-container">
|
||||
<Pivot aria-label="Successful execution of stored procedure" style={{ height: "100%" }}>
|
||||
<Pivot aria-label={t(Keys.tabs.storedProcedure.successfulExecution)} style={{ height: "100%" }}>
|
||||
<PivotItem
|
||||
headerText="Result"
|
||||
headerText={t(Keys.common.result)}
|
||||
headerButtonProps={{
|
||||
"data-order": 1,
|
||||
"data-title": "Result",
|
||||
@@ -563,11 +565,11 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
language={"javascript"}
|
||||
content={this.state.resultData}
|
||||
isReadOnly={true}
|
||||
ariaLabel={"Execute stored procedure result"}
|
||||
ariaLabel={t(Keys.tabs.storedProcedure.resultAriaLabel)}
|
||||
/>
|
||||
</PivotItem>
|
||||
<PivotItem
|
||||
headerText="console.log"
|
||||
headerText={t(Keys.tabs.storedProcedure.consoleLogTab)}
|
||||
headerButtonProps={{
|
||||
"data-order": 2,
|
||||
"data-title": "console.log",
|
||||
@@ -578,7 +580,7 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
language={"javascript"}
|
||||
content={this.state.logsData}
|
||||
isReadOnly={true}
|
||||
ariaLabel={"Execute stored procedure logs"}
|
||||
ariaLabel={t(Keys.tabs.storedProcedure.logsAriaLabel)}
|
||||
/>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
@@ -586,16 +588,16 @@ export default class StoredProcedureTabComponent extends React.Component<
|
||||
)}
|
||||
{this.state.hasErrors && (
|
||||
<div className="errors-container">
|
||||
<div className="errors-header">Errors:</div>
|
||||
<div className="errors-header">{t(Keys.tabs.storedProcedure.errors)}</div>
|
||||
<div className="errorContent">
|
||||
<span className="errorMessage">{this.state.error}</span>
|
||||
<span className="errorDetailsLink">
|
||||
<a
|
||||
aria-label="Error details link"
|
||||
aria-label={t(Keys.tabs.storedProcedure.errorDetailsAriaLabel)}
|
||||
onClick={() => this.onErrorDetailsClick()}
|
||||
onKeyPress={(event: React.KeyboardEvent<HTMLAnchorElement>) => this.onErrorDetailsKeyPress(event)}
|
||||
>
|
||||
More details
|
||||
{t(Keys.tabs.storedProcedure.moreDetails)}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,8 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
|
||||
import { createTrigger } from "../../Common/dataAccess/createTrigger";
|
||||
import { updateTrigger } from "../../Common/dataAccess/updateTrigger";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { Keys } from "../../Localization/Keys.generated";
|
||||
import { t } from "../../Localization/t";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
@@ -19,8 +21,8 @@ import { EditorReact } from "../Controls/Editor/EditorReact";
|
||||
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
|
||||
import TriggerTab from "./TriggerTab";
|
||||
const triggerTypeOptions: IDropdownOption[] = [
|
||||
{ key: "Pre", text: "Pre" },
|
||||
{ key: "Post", text: "Post" },
|
||||
{ key: "Pre", text: t(Keys.tabs.trigger.pre) },
|
||||
{ key: "Post", text: t(Keys.tabs.trigger.post) },
|
||||
];
|
||||
|
||||
const dropdownStyles: Partial<IDropdownStyles> = {
|
||||
@@ -147,10 +149,10 @@ const dropdownStyles: Partial<IDropdownStyles> = {
|
||||
};
|
||||
|
||||
const triggerOperationOptions: IDropdownOption[] = [
|
||||
{ key: "All", text: "All" },
|
||||
{ key: "Create", text: "Create" },
|
||||
{ key: "Delete", text: "Delete" },
|
||||
{ key: "Replace", text: "Replace" },
|
||||
{ key: "All", text: t(Keys.tabs.trigger.all) },
|
||||
{ key: "Create", text: t(Keys.tabs.trigger.operationCreate) },
|
||||
{ key: "Delete", text: t(Keys.tabs.trigger.operationDelete) },
|
||||
{ key: "Replace", text: t(Keys.tabs.trigger.operationReplace) },
|
||||
];
|
||||
|
||||
interface Ibutton {
|
||||
@@ -334,7 +336,7 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
||||
|
||||
protected getTabsButtons(): CommandButtonComponentProps[] {
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
const label = "Save";
|
||||
const label = t(Keys.common.save);
|
||||
if (this.saveButton.visible) {
|
||||
buttons.push({
|
||||
setState: this.setState,
|
||||
@@ -351,7 +353,7 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
||||
}
|
||||
|
||||
if (this.updateButton.visible) {
|
||||
const label = "Update";
|
||||
const label = t(Keys.common.update);
|
||||
buttons.push({
|
||||
...this,
|
||||
iconSrc: SaveIcon,
|
||||
@@ -366,7 +368,7 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
||||
}
|
||||
|
||||
if (this.discardButton.visible) {
|
||||
const label = "Discard";
|
||||
const label = t(Keys.common.discard);
|
||||
buttons.push({
|
||||
setState: this.setState,
|
||||
...this,
|
||||
@@ -415,14 +417,14 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
||||
<div className="tab-pane flexContainer trigger-form" role="tabpanel">
|
||||
<TextField
|
||||
className="trigger-field"
|
||||
label="Trigger Id"
|
||||
label={t(Keys.tabs.trigger.id)}
|
||||
id="entityTimeId"
|
||||
autoFocus
|
||||
required
|
||||
type="text"
|
||||
pattern={ValidCosmosDbIdInputPattern.source}
|
||||
title={ValidCosmosDbIdDescription}
|
||||
placeholder="Enter the new trigger id"
|
||||
placeholder={t(Keys.tabs.trigger.idPlaceholder)}
|
||||
size={40}
|
||||
value={triggerId}
|
||||
readOnly={!isIdEditable}
|
||||
@@ -446,8 +448,8 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
||||
}}
|
||||
/>
|
||||
<Dropdown
|
||||
placeholder="Trigger Type"
|
||||
label="Trigger Type"
|
||||
placeholder={t(Keys.tabs.trigger.type)}
|
||||
label={t(Keys.tabs.trigger.type)}
|
||||
options={triggerTypeOptions}
|
||||
selectedKey={triggerType}
|
||||
className="trigger-field"
|
||||
@@ -455,8 +457,8 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
||||
styles={dropdownStyles}
|
||||
/>
|
||||
<Dropdown
|
||||
placeholder="Trigger Operation"
|
||||
label="Trigger Operation"
|
||||
placeholder={t(Keys.tabs.trigger.operation)}
|
||||
label={t(Keys.tabs.trigger.operation)}
|
||||
selectedKey={triggerOperation}
|
||||
options={triggerOperationOptions}
|
||||
className="trigger-field"
|
||||
@@ -465,12 +467,12 @@ export class TriggerTabContent extends Component<TriggerTab, ITriggerTabContentS
|
||||
}
|
||||
styles={dropdownStyles}
|
||||
/>
|
||||
<Label className="trigger-field">Trigger Body</Label>
|
||||
<Label className="trigger-field">{t(Keys.tabs.trigger.body)}</Label>
|
||||
<EditorReact
|
||||
language={"json"}
|
||||
content={triggerBody}
|
||||
isReadOnly={false}
|
||||
ariaLabel={"Graph JSON"}
|
||||
ariaLabel={t(Keys.tabs.trigger.bodyAriaLabel)}
|
||||
onContentChanged={this.handleTriggerBodyChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,8 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
|
||||
import { createUserDefinedFunction } from "../../Common/dataAccess/createUserDefinedFunction";
|
||||
import { updateUserDefinedFunction } from "../../Common/dataAccess/updateUserDefinedFunction";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { Keys } from "../../Localization/Keys.generated";
|
||||
import { t } from "../../Localization/t";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||
@@ -82,7 +84,7 @@ export default class UserDefinedFunctionTabContent extends Component<
|
||||
|
||||
protected getTabsButtons(): CommandButtonComponentProps[] {
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
const label = "Save";
|
||||
const label = t(Keys.common.save);
|
||||
if (this.saveButton.visible) {
|
||||
buttons.push({
|
||||
...this,
|
||||
@@ -99,7 +101,7 @@ export default class UserDefinedFunctionTabContent extends Component<
|
||||
}
|
||||
|
||||
if (this.updateButton.visible) {
|
||||
const label = "Update";
|
||||
const label = t(Keys.common.update);
|
||||
buttons.push({
|
||||
...this,
|
||||
iconSrc: SaveIcon,
|
||||
@@ -114,7 +116,7 @@ export default class UserDefinedFunctionTabContent extends Component<
|
||||
}
|
||||
|
||||
if (this.discardButton.visible) {
|
||||
const label = "Discard";
|
||||
const label = t(Keys.common.discard);
|
||||
buttons.push({
|
||||
setState: this.setState,
|
||||
...this,
|
||||
@@ -265,7 +267,7 @@ export default class UserDefinedFunctionTabContent extends Component<
|
||||
<FluentProvider theme={currentTheme}>
|
||||
<TextField
|
||||
className="trigger-field"
|
||||
label="User Defined Function Id"
|
||||
label={t(Keys.tabs.udf.id)}
|
||||
id="entityTimeId"
|
||||
autoFocus
|
||||
required
|
||||
@@ -273,7 +275,7 @@ export default class UserDefinedFunctionTabContent extends Component<
|
||||
type="text"
|
||||
pattern={ValidCosmosDbIdInputPattern.source}
|
||||
title={ValidCosmosDbIdDescription}
|
||||
placeholder="Enter the new user defined function id"
|
||||
placeholder={t(Keys.tabs.udf.idPlaceholder)}
|
||||
size={40}
|
||||
value={udfId}
|
||||
onChange={this.handleUdfIdChange}
|
||||
@@ -299,12 +301,12 @@ export default class UserDefinedFunctionTabContent extends Component<
|
||||
}}
|
||||
/>{" "}
|
||||
</FluentProvider>
|
||||
<Label className="trigger-field">User Defined Function Body</Label>
|
||||
<Label className="trigger-field">{t(Keys.tabs.udf.body)}</Label>
|
||||
<EditorReact
|
||||
language={"javascript"}
|
||||
content={udfBody}
|
||||
isReadOnly={false}
|
||||
ariaLabel={"User defined function body"}
|
||||
ariaLabel={t(Keys.tabs.udf.bodyAriaLabel)}
|
||||
onContentChanged={this.handleUdfBodyChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -291,5 +291,126 @@
|
||||
"errorCreateContainer": "Failed to create container: {{error}}",
|
||||
"errorImportData": "Failed to import data: {{error}}"
|
||||
}
|
||||
},
|
||||
"contextMenu": {
|
||||
"newContainer": "New {{containerName}}",
|
||||
"restoreContainer": "Restore {{containerName}}",
|
||||
"deleteDatabase": "Delete {{databaseName}}",
|
||||
"deleteContainer": "Delete {{containerName}}",
|
||||
"newSqlQuery": "New SQL Query",
|
||||
"newQuery": "New Query",
|
||||
"openMongoShell": "Open Mongo Shell",
|
||||
"newShell": "New Shell",
|
||||
"openCassandraShell": "Open Cassandra Shell",
|
||||
"newStoredProcedure": "New Stored Procedure",
|
||||
"newUdf": "New UDF",
|
||||
"newTrigger": "New Trigger",
|
||||
"deleteStoredProcedure": "Delete Stored Procedure",
|
||||
"deleteTrigger": "Delete Trigger",
|
||||
"deleteUdf": "Delete User Defined Function"
|
||||
},
|
||||
"tabs": {
|
||||
"documents": {
|
||||
"newItem": "New Item",
|
||||
"newDocument": "New Document",
|
||||
"uploadItem": "Upload Item",
|
||||
"applyFilter": "Apply Filter",
|
||||
"unsavedChanges": "Unsaved changes",
|
||||
"unsavedChangesMessage": "Your unsaved changes will be lost. Do you want to continue?",
|
||||
"createDocumentFailed": "Create document failed",
|
||||
"updateDocumentFailed": "Update document failed",
|
||||
"documentDeleted": "Document successfully deleted.",
|
||||
"deleteDocumentDialogTitle": "Delete document",
|
||||
"deleteDocumentsDialogTitle": "Delete documents",
|
||||
"throttlingError": "Some documents failed to delete due to a rate limiting error. Please try again later. To prevent this in the future, consider increasing the throughput on your container or database.",
|
||||
"deleteFailed": "Deleting document(s) failed ({{error}})",
|
||||
"missingShardProperty": "The document is lacking the shard property: {{partitionKeyProperty}}",
|
||||
"refreshGridFailed": "Refresh documents grid failed",
|
||||
"confirmDelete": "Are you sure you want to delete {{documentName}}?",
|
||||
"confirmDeleteTitle": "Confirm delete",
|
||||
"selectedItems": "the selected {{count}} items",
|
||||
"selectedItem": "the selected item",
|
||||
"selectedDocuments": "the selected {{count}} documents",
|
||||
"selectedDocument": "the selected document",
|
||||
"deleteDocumentFailedLog": "Failed to delete document {{documentId}} with status code {{statusCode}}",
|
||||
"deleteSuccessLog": "Successfully deleted {{count}} document(s)",
|
||||
"deleteThrottledLog": "Failed to delete {{count}} document(s) due to \"Request too large\" (429) error. Retrying...",
|
||||
"missingShardKeyLog": "Failed to save new document: Document shard key not defined",
|
||||
"filterTooltip": "Type a query predicate or choose one from the list.",
|
||||
"loadMore": "Load more",
|
||||
"documentEditor": "Document editor",
|
||||
"savedFilters": "Saved filters",
|
||||
"defaultFilters": "Default filters",
|
||||
"abort": "Abort",
|
||||
"deletingDocuments": "Deleting {{count}} document(s)",
|
||||
"deletedDocumentsSuccess": "Successfully deleted {{count}} document(s).",
|
||||
"deleteAborted": "Deleting document(s) was aborted.",
|
||||
"failedToDeleteDocuments": "Failed to delete {{count}} document(s).",
|
||||
"requestTooLargeBase": "Some delete requests failed due to a \"Request too large\" exception (429)",
|
||||
"retriedSuccessfully": "but were successfully retried.",
|
||||
"retryingNow": "Retrying now.",
|
||||
"increaseThroughputTip": "To prevent this in the future, consider increasing the throughput on your container or database.",
|
||||
"numberOfSelectedDocuments": "Number of selected documents: {{count}}",
|
||||
"mongoFilterPlaceholder": "Type a query predicate (e.g., {\"id\":\"foo\"}), or choose one from the drop down list, or leave empty to query all documents.",
|
||||
"sqlFilterPlaceholder": "Type a query predicate (e.g., WHERE c.id=\"1\"), or choose one from the drop down list, or leave empty to query all documents.",
|
||||
"error": "Error",
|
||||
"warning": "Warning"
|
||||
},
|
||||
"query": {
|
||||
"executeQuery": "Execute Query",
|
||||
"executeSelection": "Execute Selection",
|
||||
"saveQuery": "Save Query",
|
||||
"downloadQuery": "Download Query",
|
||||
"cancelQuery": "Cancel query",
|
||||
"openSavedQueries": "Open Saved Queries",
|
||||
"vertical": "Vertical",
|
||||
"horizontal": "Horizontal",
|
||||
"view": "View",
|
||||
"editingQuery": "Editing Query"
|
||||
},
|
||||
"storedProcedure": {
|
||||
"id": "Stored Procedure Id",
|
||||
"idPlaceholder": "Enter the new stored procedure id",
|
||||
"idAriaLabel": "Stored procedure id",
|
||||
"body": "Stored Procedure Body",
|
||||
"bodyAriaLabel": "Stored procedure body",
|
||||
"successfulExecution": "Successful execution of stored procedure",
|
||||
"resultAriaLabel": "Execute stored procedure result",
|
||||
"logsAriaLabel": "Execute stored procedure logs",
|
||||
"errors": "Errors:",
|
||||
"errorDetailsAriaLabel": "Error details link",
|
||||
"moreDetails": "More details",
|
||||
"consoleLogTab": "console.log"
|
||||
},
|
||||
"trigger": {
|
||||
"id": "Trigger Id",
|
||||
"idPlaceholder": "Enter the new trigger id",
|
||||
"type": "Trigger Type",
|
||||
"operation": "Trigger Operation",
|
||||
"body": "Trigger Body",
|
||||
"bodyAriaLabel": "Trigger body",
|
||||
"pre": "Pre",
|
||||
"post": "Post",
|
||||
"all": "All",
|
||||
"operationCreate": "Create",
|
||||
"operationDelete": "Delete",
|
||||
"operationReplace": "Replace"
|
||||
},
|
||||
"udf": {
|
||||
"id": "User Defined Function Id",
|
||||
"idPlaceholder": "Enter the new user defined function id",
|
||||
"body": "User Defined Function Body",
|
||||
"bodyAriaLabel": "User defined function body"
|
||||
},
|
||||
"conflicts": {
|
||||
"unsavedChanges": "Unsaved changes",
|
||||
"changesWillBeLost": "Changes will be lost. Do you want to continue?",
|
||||
"resolveConflictFailed": "Resolve conflict failed",
|
||||
"deleteConflictFailed": "Delete conflict failed",
|
||||
"refreshGridFailed": "Refresh documents grid failed"
|
||||
},
|
||||
"mongoShell": {
|
||||
"title": "Mongo Shell"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,14 @@ import Adapter from "enzyme-adapter-react-16";
|
||||
import "jest-canvas-mock";
|
||||
import enableHooks from "jest-react-hooks-shallow";
|
||||
import { TextDecoder, TextEncoder } from "util";
|
||||
import i18n from "./i18n";
|
||||
import enResources from "./Localization/en/Resources.json";
|
||||
configure({ adapter: new Adapter() });
|
||||
initializeIcons();
|
||||
|
||||
// Load English translations synchronously so t() returns real values in tests
|
||||
i18n.addResourceBundle("en", "Resources", enResources, true, true);
|
||||
|
||||
if (typeof window.URL.createObjectURL === "undefined") {
|
||||
Object.defineProperty(window.URL, "createObjectURL", { value: () => {} });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user