mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-04-18 12:29:48 +01:00
Add localization strings batch 4 (#2420)
* Add localization strings batch 4 * run prettier * Update test snap * Remove duplicates
This commit is contained in:
@@ -4,6 +4,8 @@ import { titleAndInputStackProps, unsavedEditorWarningMessage } from "Explorer/C
|
||||
import { isDirty } from "Explorer/Controls/Settings/SettingsUtils";
|
||||
import { loadMonaco } from "Explorer/LazyMonaco";
|
||||
import { monacoTheme, useThemeStore } from "hooks/useTheme";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
export interface ComputedPropertiesComponentProps {
|
||||
@@ -107,7 +109,7 @@ export class ComputedPropertiesComponent extends React.Component<
|
||||
this.computedPropertiesEditor = monaco.editor.create(this.computedPropertiesDiv.current, {
|
||||
value: value,
|
||||
language: "json",
|
||||
ariaLabel: "Computed properties",
|
||||
ariaLabel: t(Keys.controls.settings.computedProperties.ariaLabel),
|
||||
theme: monacoTheme(),
|
||||
});
|
||||
if (this.computedPropertiesEditor) {
|
||||
@@ -151,9 +153,9 @@ export class ComputedPropertiesComponent extends React.Component<
|
||||
)}
|
||||
<Text style={{ marginLeft: "30px", marginBottom: "10px", color: "var(--colorNeutralForeground1)" }}>
|
||||
<Link target="_blank" href="https://aka.ms/computed-properties-preview/">
|
||||
{"Learn more"} <FontIcon iconName="NavigateExternalInline" />
|
||||
{t(Keys.common.learnMore)} <FontIcon iconName="NavigateExternalInline" />
|
||||
</Link>
|
||||
  about how to define computed properties and how to use them.
|
||||
  {t(Keys.controls.settings.computedProperties.learnMorePrefix)}
|
||||
</Text>
|
||||
<div
|
||||
className="settingsV2Editor"
|
||||
|
||||
@@ -2,6 +2,8 @@ import { ChoiceGroup, IChoiceGroupOption, ITextFieldProps, Stack, TextField } fr
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
import {
|
||||
conflictResolutionCustomToolTip,
|
||||
conflictResolutionLwwTooltip,
|
||||
@@ -32,9 +34,12 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
private conflictResolutionChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{
|
||||
key: DataModels.ConflictResolutionMode.LastWriterWins,
|
||||
text: "Last Write Wins (default)",
|
||||
text: t(Keys.controls.settings.conflictResolution.lwwDefault),
|
||||
},
|
||||
{
|
||||
key: DataModels.ConflictResolutionMode.Custom,
|
||||
text: t(Keys.controls.settings.conflictResolution.customMergeProcedure),
|
||||
},
|
||||
{ key: DataModels.ConflictResolutionMode.Custom, text: "Merge Procedure (custom)" },
|
||||
];
|
||||
|
||||
componentDidMount(): void {
|
||||
@@ -85,7 +90,7 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
|
||||
private getConflictResolutionModeComponent = (): JSX.Element => (
|
||||
<ChoiceGroup
|
||||
label="Mode"
|
||||
label={t(Keys.controls.settings.conflictResolution.mode)}
|
||||
selectedKey={this.props.conflictResolutionPolicyMode}
|
||||
options={this.conflictResolutionChoiceGroupOptions}
|
||||
onChange={this.onConflictResolutionPolicyModeChange}
|
||||
@@ -103,7 +108,7 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
private getConflictResolutionLWWComponent = (): JSX.Element => (
|
||||
<TextField
|
||||
id="conflictResolutionLwwTextField"
|
||||
label={"Conflict Resolver Property"}
|
||||
label={t(Keys.controls.settings.conflictResolution.conflictResolverProperty)}
|
||||
onRenderLabel={this.onRenderLwwComponentTextField}
|
||||
styles={{
|
||||
fieldGroup: {
|
||||
@@ -158,7 +163,7 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
return (
|
||||
<TextField
|
||||
id="conflictResolutionCustomTextField"
|
||||
label="Stored procedure"
|
||||
label={t(Keys.controls.settings.conflictResolution.storedProcedure)}
|
||||
onRenderLabel={this.onRenderCustomComponentTextField}
|
||||
styles={{
|
||||
fieldGroup: {
|
||||
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
import { titleAndInputStackProps } from "Explorer/Controls/Settings/SettingsRenderUtils";
|
||||
import { ContainerPolicyTabTypes, isDirty } from "Explorer/Controls/Settings/SettingsUtils";
|
||||
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
import React from "react";
|
||||
|
||||
export interface ContainerPolicyComponentProps {
|
||||
@@ -153,7 +155,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
|
||||
<PivotItem
|
||||
itemKey={ContainerPolicyTabTypes[ContainerPolicyTabTypes.VectorPolicyTab]}
|
||||
style={{ marginTop: 20, color: "var(--colorNeutralForeground1)" }}
|
||||
headerText="Vector Policy"
|
||||
headerText={t(Keys.controls.settings.containerPolicy.vectorPolicy)}
|
||||
>
|
||||
<Stack {...titleAndInputStackProps} styles={{ root: { position: "relative", maxWidth: "400px" } }}>
|
||||
{vectorEmbeddings && (
|
||||
@@ -175,7 +177,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
|
||||
<PivotItem
|
||||
itemKey={ContainerPolicyTabTypes[ContainerPolicyTabTypes.FullTextPolicyTab]}
|
||||
style={{ marginTop: 20, color: "var(--colorNeutralForeground1)" }}
|
||||
headerText="Full Text Policy"
|
||||
headerText={t(Keys.controls.settings.containerPolicy.fullTextPolicy)}
|
||||
>
|
||||
<Stack {...titleAndInputStackProps} styles={{ root: { position: "relative", maxWidth: "400px" } }}>
|
||||
{fullTextSearchPolicy ? (
|
||||
@@ -218,7 +220,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
|
||||
});
|
||||
}}
|
||||
>
|
||||
Create new full text search policy
|
||||
{t(Keys.controls.settings.containerPolicy.createFullTextPolicy)}
|
||||
</DefaultButton>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -2,6 +2,8 @@ import { MessageBar, MessageBarType, Stack } from "@fluentui/react";
|
||||
import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
import { loadMonaco } from "../../../LazyMonaco";
|
||||
import { titleAndInputStackProps, unsavedEditorWarningMessage } from "../SettingsRenderUtils";
|
||||
import { isDirty as isContentDirty, isDataMaskingEnabled } from "../SettingsUtils";
|
||||
@@ -89,7 +91,7 @@ export class DataMaskingComponent extends React.Component<DataMaskingComponentPr
|
||||
value: value,
|
||||
language: "json",
|
||||
automaticLayout: true,
|
||||
ariaLabel: "Data Masking Policy",
|
||||
ariaLabel: t(Keys.controls.settings.dataMasking.ariaLabel),
|
||||
fontSize: 13,
|
||||
minimap: { enabled: false },
|
||||
wordWrap: "off",
|
||||
@@ -142,7 +144,7 @@ export class DataMaskingComponent extends React.Component<DataMaskingComponentPr
|
||||
)}
|
||||
{this.props.validationErrors.length > 0 && (
|
||||
<MessageBar messageBarType={MessageBarType.error}>
|
||||
Validation failed: {this.props.validationErrors.join(", ")}
|
||||
{t(Keys.controls.settings.dataMasking.validationFailed)} {this.props.validationErrors.join(", ")}
|
||||
</MessageBar>
|
||||
)}
|
||||
<div className="settingsV2Editor" tabIndex={0} ref={this.dataMaskingDiv}></div>
|
||||
|
||||
@@ -2,6 +2,8 @@ import { FontIcon, Link, Stack, Text } from "@fluentui/react";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import React from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
import { GlobalSecondaryIndexSourceComponent } from "./GlobalSecondaryIndexSourceComponent";
|
||||
import { GlobalSecondaryIndexTargetComponent } from "./GlobalSecondaryIndexTargetComponent";
|
||||
|
||||
@@ -21,7 +23,9 @@ export const GlobalSecondaryIndexComponent: React.FC<GlobalSecondaryIndexCompone
|
||||
<Stack tokens={{ childrenGap: 8 }} styles={{ root: { maxWidth: 600 } }}>
|
||||
<Stack horizontal verticalAlign="center" wrap tokens={{ childrenGap: 8 }}>
|
||||
{isSourceContainer && (
|
||||
<Text styles={{ root: { fontWeight: 600 } }}>This container has the following indexes defined for it.</Text>
|
||||
<Text styles={{ root: { fontWeight: 600 } }}>
|
||||
{t(Keys.controls.settings.globalSecondaryIndex.indexesDefined)}
|
||||
</Text>
|
||||
)}
|
||||
<Text>
|
||||
<Link
|
||||
@@ -31,7 +35,7 @@ export const GlobalSecondaryIndexComponent: React.FC<GlobalSecondaryIndexCompone
|
||||
Learn more
|
||||
<FontIcon iconName="NavigateExternalInline" style={{ marginLeft: "4px" }} />
|
||||
</Link>{" "}
|
||||
about how to define global secondary indexes and how to use them.
|
||||
{t(Keys.controls.settings.globalSecondaryIndex.learnMoreSuffix)}
|
||||
</Text>
|
||||
</Stack>
|
||||
{isSourceContainer && <GlobalSecondaryIndexSourceComponent collection={collection} explorer={explorer} />}
|
||||
|
||||
@@ -9,6 +9,8 @@ import { useSidePanel } from "hooks/useSidePanel";
|
||||
import * as monaco from "monaco-editor";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
|
||||
export interface GlobalSecondaryIndexSourceComponentProps {
|
||||
collection: ViewModels.Collection;
|
||||
@@ -67,7 +69,7 @@ export const GlobalSecondaryIndexSourceComponent: React.FC<GlobalSecondaryIndexS
|
||||
editorRef.current = monacoInstance.editor.create(editorContainerRef.current, {
|
||||
value: jsonValue,
|
||||
language: "json",
|
||||
ariaLabel: "Global Secondary Index JSON",
|
||||
ariaLabel: t(Keys.controls.settings.globalSecondaryIndex.jsonAriaLabel),
|
||||
readOnly: true,
|
||||
});
|
||||
};
|
||||
@@ -98,7 +100,7 @@ export const GlobalSecondaryIndexSourceComponent: React.FC<GlobalSecondaryIndexS
|
||||
}}
|
||||
/>
|
||||
<PrimaryButton
|
||||
text="Add index"
|
||||
text={t(Keys.controls.settings.globalSecondaryIndex.addIndex)}
|
||||
styles={{ root: { width: "fit-content", marginTop: 12 } }}
|
||||
onClick={() =>
|
||||
useSidePanel
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Stack, Text } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
|
||||
export interface GlobalSecondaryIndexTargetComponentProps {
|
||||
collection: ViewModels.Collection;
|
||||
@@ -25,17 +27,21 @@ export const GlobalSecondaryIndexTargetComponent: React.FC<GlobalSecondaryIndexT
|
||||
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: 15 }} styles={{ root: { maxWidth: 600 } }}>
|
||||
<Text styles={textHeadingStyle}>Global Secondary Index Settings</Text>
|
||||
<Text styles={textHeadingStyle}>{t(Keys.controls.settings.globalSecondaryIndex.settingsTitle)}</Text>
|
||||
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Text styles={{ root: { fontWeight: "600" } }}>Source container</Text>
|
||||
<Text styles={{ root: { fontWeight: "600" } }}>
|
||||
{t(Keys.controls.settings.globalSecondaryIndex.sourceContainer)}
|
||||
</Text>
|
||||
<Stack styles={valueBoxStyle}>
|
||||
<Text>{globalSecondaryIndexDefinition?.sourceCollectionId}</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Text styles={{ root: { fontWeight: "600" } }}>Global secondary index definition</Text>
|
||||
<Text styles={{ root: { fontWeight: "600" } }}>
|
||||
{t(Keys.controls.settings.globalSecondaryIndex.indexDefinition)}
|
||||
</Text>
|
||||
<Stack styles={valueBoxStyle}>
|
||||
<Text>{globalSecondaryIndexDefinition?.definition}</Text>
|
||||
</Stack>
|
||||
|
||||
@@ -3,6 +3,8 @@ import { monacoTheme, useThemeStore } from "hooks/useTheme";
|
||||
import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
import { loadMonaco } from "../../../LazyMonaco";
|
||||
import { titleAndInputStackProps, unsavedEditorWarningMessage } from "../SettingsRenderUtils";
|
||||
import { isDirty, isIndexTransforming } from "../SettingsUtils";
|
||||
@@ -119,7 +121,7 @@ export class IndexingPolicyComponent extends React.Component<
|
||||
value: value,
|
||||
language: "json",
|
||||
readOnly: isIndexTransforming(this.props.indexTransformationProgress),
|
||||
ariaLabel: "Indexing Policy",
|
||||
ariaLabel: t(Keys.controls.settings.indexingPolicy.ariaLabel),
|
||||
theme: monacoTheme(),
|
||||
});
|
||||
if (this.indexingPolicyEditor) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { MessageBar, MessageBarType } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { handleError } from "../../../../../Common/ErrorHandlingUtils";
|
||||
import { Keys } from "../../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../../Localization/t";
|
||||
import {
|
||||
mongoIndexTransformationRefreshingMessage,
|
||||
renderMongoIndexTransformationRefreshMessage,
|
||||
@@ -46,7 +48,11 @@ export class IndexingPolicyRefreshComponent extends React.Component<
|
||||
try {
|
||||
await this.props.refreshIndexTransformationProgress();
|
||||
} catch (error) {
|
||||
handleError(error, "RefreshIndexTransformationProgress", "Refreshing index transformation progress failed");
|
||||
handleError(
|
||||
error,
|
||||
"RefreshIndexTransformationProgress",
|
||||
t(Keys.controls.settings.indexingPolicyRefresh.refreshFailed),
|
||||
);
|
||||
} finally {
|
||||
this.setState({ isRefreshing: false });
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ exports[`IndexingPolicyRefreshComponent renders 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
You can make more indexing changes once the current index transformation has completed. It is 90% complete.
|
||||
You can make more indexing changes once the current index transformation has completed. It is 90% complete.
|
||||
<StyledLinkBase
|
||||
onClick={[Function]}
|
||||
>
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
IDropdownOption,
|
||||
ITextField,
|
||||
} from "@fluentui/react";
|
||||
import { Keys } from "../../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../../Localization/t";
|
||||
import {
|
||||
addMongoIndexSubElementsTokens,
|
||||
mongoErrorMessageStyles,
|
||||
@@ -66,7 +68,7 @@ export class AddMongoIndexComponent extends React.Component<AddMongoIndexCompone
|
||||
<Stack {...mongoWarningStackProps}>
|
||||
<Stack horizontal tokens={addMongoIndexSubElementsTokens}>
|
||||
<TextField
|
||||
ariaLabel={"Index Field Name " + this.props.position}
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.indexFieldName) + " " + this.props.position}
|
||||
disabled={this.props.disabled}
|
||||
styles={shortWidthTextFieldStyles}
|
||||
componentRef={this.setRef}
|
||||
@@ -76,17 +78,17 @@ export class AddMongoIndexComponent extends React.Component<AddMongoIndexCompone
|
||||
/>
|
||||
|
||||
<Dropdown
|
||||
ariaLabel={"Index Type " + this.props.position}
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.indexType) + " " + this.props.position}
|
||||
disabled={this.props.disabled}
|
||||
styles={shortWidthDropDownStyles}
|
||||
placeholder="Select an index type"
|
||||
placeholder={t(Keys.controls.settings.mongoIndexing.selectIndexType)}
|
||||
selectedKey={this.props.type}
|
||||
options={this.indexTypes}
|
||||
onChange={this.onTypeChange}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
ariaLabel={"Undo Button " + this.props.position}
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.undoButton) + " " + this.props.position}
|
||||
iconProps={{ iconName: "Undo" }}
|
||||
disabled={!this.props.description && !this.props.type}
|
||||
onClick={() => this.props.onDiscard()}
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
} from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { MongoIndex } from "../../../../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { Keys } from "../../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../../Localization/t";
|
||||
import { CollapsibleSectionComponent } from "../../../CollapsiblePanel/CollapsibleSectionComponent";
|
||||
import {
|
||||
addMongoIndexStackProps,
|
||||
@@ -83,11 +85,25 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
};
|
||||
|
||||
private initialIndexesColumns: IColumn[] = [
|
||||
{ key: "definition", name: "Definition", fieldName: "definition", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{ key: "type", name: "Type", fieldName: "type", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{
|
||||
key: "definition",
|
||||
name: t(Keys.controls.settings.mongoIndexing.definitionColumn),
|
||||
fieldName: "definition",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
},
|
||||
{
|
||||
key: "type",
|
||||
name: t(Keys.controls.settings.mongoIndexing.typeColumn),
|
||||
fieldName: "type",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
},
|
||||
{
|
||||
key: "actionButton",
|
||||
name: "Drop Index",
|
||||
name: t(Keys.controls.settings.mongoIndexing.dropIndexColumn),
|
||||
fieldName: "actionButton",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
@@ -96,11 +112,25 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
];
|
||||
|
||||
private indexesToBeDroppedColumns: IColumn[] = [
|
||||
{ key: "definition", name: "Definition", fieldName: "definition", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{ key: "type", name: "Type", fieldName: "type", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{
|
||||
key: "definition",
|
||||
name: t(Keys.controls.settings.mongoIndexing.definitionColumn),
|
||||
fieldName: "definition",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
},
|
||||
{
|
||||
key: "type",
|
||||
name: t(Keys.controls.settings.mongoIndexing.typeColumn),
|
||||
fieldName: "type",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
},
|
||||
{
|
||||
key: "actionButton",
|
||||
name: "Add index back",
|
||||
name: t(Keys.controls.settings.mongoIndexing.addIndexBackColumn),
|
||||
fieldName: "actionButton",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
@@ -161,7 +191,7 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
private getActionButton = (arrayPosition: number, isCurrentIndex: boolean): JSX.Element => {
|
||||
return isCurrentIndex ? (
|
||||
<IconButton
|
||||
ariaLabel="Delete index Button"
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.deleteIndexButton)}
|
||||
iconProps={{ iconName: "Delete" }}
|
||||
disabled={isIndexTransforming(this.props.indexTransformationProgress)}
|
||||
onClick={() => {
|
||||
@@ -170,7 +200,7 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
/>
|
||||
) : (
|
||||
<IconButton
|
||||
ariaLabel="Add back Index Button"
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.addBackIndexButton)}
|
||||
iconProps={{ iconName: "Add" }}
|
||||
onClick={() => {
|
||||
this.props.onRevertIndexDrop(arrayPosition);
|
||||
@@ -258,7 +288,10 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
|
||||
return (
|
||||
<Stack {...createAndAddMongoIndexStackProps} styles={mediumWidthStackStyles}>
|
||||
<CollapsibleSectionComponent title="Current index(es)" isExpandedByDefault={true}>
|
||||
<CollapsibleSectionComponent
|
||||
title={t(Keys.controls.settings.mongoIndexing.currentIndexes)}
|
||||
isExpandedByDefault={true}
|
||||
>
|
||||
{
|
||||
<>
|
||||
<DetailsList
|
||||
@@ -285,7 +318,10 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
|
||||
return (
|
||||
<Stack styles={mediumWidthStackStyles}>
|
||||
<CollapsibleSectionComponent title="Index(es) to be dropped" isExpandedByDefault={true}>
|
||||
<CollapsibleSectionComponent
|
||||
title={t(Keys.controls.settings.mongoIndexing.indexesToBeDropped)}
|
||||
isExpandedByDefault={true}
|
||||
>
|
||||
{indexesToBeDropped.length > 0 && (
|
||||
<DetailsList
|
||||
styles={customDetailsListStyles}
|
||||
|
||||
@@ -18,6 +18,8 @@ import { cancelDataTransferJob, pollDataTransferJob } from "Common/dataAccess/da
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { ChangePartitionKeyPane } from "Explorer/Panes/ChangePartitionKeyPane/ChangePartitionKeyPane";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
import {
|
||||
CosmosSqlDataTransferDataSourceSink,
|
||||
DataTransferJobGetResults,
|
||||
@@ -80,7 +82,7 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
return (collection.partitionKeyProperties || []).map((property) => "/" + property).join(", ");
|
||||
};
|
||||
|
||||
const partitionKeyName = "Partition key";
|
||||
const partitionKeyName = t(Keys.controls.settings.partitionKey.partitionKey);
|
||||
const partitionKeyValue = getPartitionKeyValue();
|
||||
|
||||
const textHeadingStyle = {
|
||||
@@ -148,7 +150,13 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
const getProgressDescription = (): string => {
|
||||
const processedCount = portalDataTransferJob?.properties?.processedCount;
|
||||
const totalCount = portalDataTransferJob?.properties?.totalCount;
|
||||
const processedCountString = totalCount > 0 ? `(${processedCount} of ${totalCount} documents processed)` : "";
|
||||
const processedCountString =
|
||||
totalCount > 0
|
||||
? t(Keys.controls.settings.partitionKeyEditor.documentsProcessed, {
|
||||
processedCount: String(processedCount),
|
||||
totalCount: String(totalCount),
|
||||
})
|
||||
: "";
|
||||
return `${portalDataTransferJob?.properties?.status} ${processedCountString}`;
|
||||
};
|
||||
|
||||
@@ -181,16 +189,28 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: 20 }} styles={{ root: { maxWidth: 600 } }}>
|
||||
<Stack tokens={{ childrenGap: 10 }}>
|
||||
{!isReadOnly && <Text styles={textHeadingStyle}>Change {partitionKeyName.toLowerCase()}</Text>}
|
||||
{!isReadOnly && (
|
||||
<Text styles={textHeadingStyle}>
|
||||
{t(Keys.controls.settings.partitionKeyEditor.changePartitionKey, {
|
||||
partitionKeyName: partitionKeyName.toLowerCase(),
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
<Stack horizontal tokens={{ childrenGap: 20 }}>
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Text styles={textSubHeadingStyle}>Current {partitionKeyName.toLowerCase()}</Text>
|
||||
<Text styles={textSubHeadingStyle}>Partitioning</Text>
|
||||
<Text styles={textSubHeadingStyle}>
|
||||
{t(Keys.controls.settings.partitionKeyEditor.currentPartitionKey, {
|
||||
partitionKeyName: partitionKeyName.toLowerCase(),
|
||||
})}
|
||||
</Text>
|
||||
<Text styles={textSubHeadingStyle}>{t(Keys.controls.settings.partitionKeyEditor.partitioning)}</Text>
|
||||
</Stack>
|
||||
<Stack tokens={{ childrenGap: 5 }} data-test="partition-key-values">
|
||||
<Text styles={textSubHeadingStyle1}>{partitionKeyValue}</Text>
|
||||
<Text styles={textSubHeadingStyle1}>
|
||||
{isHierarchicalPartitionedContainer() ? "Hierarchical" : "Non-hierarchical"}
|
||||
{isHierarchicalPartitionedContainer()
|
||||
? t(Keys.controls.settings.partitionKeyEditor.hierarchical)
|
||||
: t(Keys.controls.settings.partitionKeyEditor.nonHierarchical)}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -204,33 +224,33 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||
styles={darkThemeMessageBarStyles}
|
||||
>
|
||||
To safeguard the integrity of the data being copied to the new container, ensure that no updates are made to
|
||||
the source container for the entire duration of the partition key change process.
|
||||
{t(Keys.controls.settings.partitionKeyEditor.safeguardWarning)}
|
||||
<Link
|
||||
href="https://learn.microsoft.com/azure/cosmos-db/container-copy#how-does-container-copy-work"
|
||||
target="_blank"
|
||||
underline
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
>
|
||||
Learn more
|
||||
{t(Keys.common.learnMore)}
|
||||
</Link>
|
||||
</MessageBar>
|
||||
<Text styles={{ root: { color: "var(--colorNeutralForeground1)" } }}>
|
||||
To change the partition key, a new destination container must be created or an existing destination
|
||||
container selected. Data will then be copied to the destination container.
|
||||
{t(Keys.controls.settings.partitionKeyEditor.changeDescription)}
|
||||
</Text>
|
||||
{configContext.platform !== Platform.Emulator && (
|
||||
<PrimaryButton
|
||||
data-test="change-partition-key-button"
|
||||
styles={{ root: { width: "fit-content" } }}
|
||||
text="Change"
|
||||
text={t(Keys.controls.settings.partitionKeyEditor.changeButton)}
|
||||
onClick={startPartitionkeyChangeWorkflow}
|
||||
disabled={isCurrentJobInProgress(portalDataTransferJob)}
|
||||
/>
|
||||
)}
|
||||
{portalDataTransferJob && (
|
||||
<Stack>
|
||||
<Text styles={textHeadingStyle}>{partitionKeyName} change job</Text>
|
||||
<Text styles={textHeadingStyle}>
|
||||
{t(Keys.controls.settings.partitionKeyEditor.changeJob, { partitionKeyName })}
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal
|
||||
tokens={{ childrenGap: 20 }}
|
||||
@@ -251,7 +271,10 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
}}
|
||||
></ProgressIndicator>
|
||||
{isCurrentJobInProgress(portalDataTransferJob) && (
|
||||
<DefaultButton text="Cancel" onClick={() => cancelRunningDataTransferJob(portalDataTransferJob)} />
|
||||
<DefaultButton
|
||||
text={t(Keys.controls.settings.partitionKeyEditor.cancelButton)}
|
||||
onClick={() => cancelRunningDataTransferJob(portalDataTransferJob)}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -4,6 +4,8 @@ import * as Constants from "../../../../Common/Constants";
|
||||
import { Platform, configContext } from "../../../../ConfigContext";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
import * as SharedConstants from "../../../../Shared/Constants";
|
||||
import { userContext } from "../../../../UserContext";
|
||||
import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils";
|
||||
@@ -156,14 +158,12 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
const freeTierLimits = SharedConstants.FreeTierLimits;
|
||||
return (
|
||||
<Text>
|
||||
With free tier, you will get the first {freeTierLimits.RU} RU/s and {freeTierLimits.Storage} GB of storage in
|
||||
this account for free. To keep your account free, keep the total RU/s across all resources in the account to{" "}
|
||||
{freeTierLimits.RU} RU/s.
|
||||
{t(Keys.controls.settings.scale.freeTierInfo, { ru: freeTierLimits.RU, storage: freeTierLimits.Storage })}
|
||||
<Link
|
||||
href="https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill#billing-examples-with-free-tier-accounts"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more.
|
||||
{t(Keys.controls.settings.scale.freeTierLearnMore)}
|
||||
</Link>
|
||||
</Text>
|
||||
);
|
||||
@@ -188,12 +188,9 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
{/* TODO: Replace link with call to the Azure Support blade */}
|
||||
{this.isAutoScaleEnabled() && (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<Text>Throughput (RU/s)</Text>
|
||||
<Text>{t(Keys.controls.settings.scale.throughputRuS)}</Text>
|
||||
<TextField disabled styles={getTextFieldStyles(undefined, undefined)} />
|
||||
<Text>
|
||||
Your account has custom settings that prevents setting throughput at the container level. Please work with
|
||||
your Cosmos DB engineering team point of contact to make changes.
|
||||
</Text>
|
||||
<Text>{t(Keys.controls.settings.scale.autoScaleCustomSettings)}</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
} from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../Localization/t";
|
||||
import { userContext } from "../../../../UserContext";
|
||||
import { Int32 } from "../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
|
||||
import {
|
||||
@@ -85,9 +87,12 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
constructor(props: SubSettingsComponentProps) {
|
||||
super(props);
|
||||
this.geospatialVisible = userContext.apiType === "SQL";
|
||||
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
||||
this.partitionKeyName =
|
||||
userContext.apiType === "Mongo"
|
||||
? t(Keys.controls.settings.partitionKey.shardKey)
|
||||
: t(Keys.controls.settings.partitionKey.partitionKey);
|
||||
this.partitionKeyValue = this.getPartitionKeyValue();
|
||||
this.uniqueKeyName = "Unique keys";
|
||||
this.uniqueKeyName = t(Keys.controls.settings.subSettings.uniqueKeys);
|
||||
this.uniqueKeyValue = this.getUniqueKeyValue();
|
||||
}
|
||||
|
||||
@@ -143,9 +148,13 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
};
|
||||
|
||||
private ttlChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{ key: TtlType.Off, text: "Off", ariaLabel: "ttl-off-option" },
|
||||
{ key: TtlType.OnNoDefault, text: "On (no default)", ariaLabel: "ttl-on-no-default-option" },
|
||||
{ key: TtlType.On, text: "On", ariaLabel: "ttl-on-option" },
|
||||
{ key: TtlType.Off, text: t(Keys.controls.settings.subSettings.ttlOff), ariaLabel: "ttl-off-option" },
|
||||
{
|
||||
key: TtlType.OnNoDefault,
|
||||
text: t(Keys.controls.settings.subSettings.ttlOnNoDefault),
|
||||
ariaLabel: "ttl-on-no-default-option",
|
||||
},
|
||||
{ key: TtlType.On, text: t(Keys.controls.settings.subSettings.ttlOn), ariaLabel: "ttl-on-option" },
|
||||
];
|
||||
|
||||
public getTtlValue = (value: string): TtlType => {
|
||||
@@ -216,13 +225,13 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
To enable time-to-live (TTL) for your collection/documents,{" "}
|
||||
{t(Keys.controls.settings.subSettings.mongoTtlMessage)}{" "}
|
||||
<Link
|
||||
href="https://docs.microsoft.com/en-us/azure/cosmos-db/mongodb-time-to-live"
|
||||
target="_blank"
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
>
|
||||
create a TTL index
|
||||
{t(Keys.controls.settings.subSettings.mongoTtlLinkText)}
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
@@ -231,7 +240,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<ChoiceGroup
|
||||
id="timeToLive"
|
||||
label="Time to Live"
|
||||
label={t(Keys.controls.settings.subSettings.timeToLive)}
|
||||
selectedKey={this.props.timeToLive}
|
||||
options={this.ttlChoiceGroupOptions}
|
||||
onChange={this.onTtlChange}
|
||||
@@ -255,8 +264,8 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
max={Int32.Max}
|
||||
value={this.props.displayedTtlSeconds}
|
||||
onChange={this.onTimeToLiveSecondsChange}
|
||||
suffix="second(s)"
|
||||
ariaLabel={`Time to live in seconds`}
|
||||
suffix={t(Keys.controls.settings.subSettings.seconds)}
|
||||
ariaLabel={t(Keys.controls.settings.subSettings.timeToLiveInSeconds)}
|
||||
data-test="ttl-input"
|
||||
/>
|
||||
)}
|
||||
@@ -264,16 +273,16 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
);
|
||||
|
||||
private analyticalTtlChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{ key: TtlType.Off, text: "Off", disabled: true },
|
||||
{ key: TtlType.OnNoDefault, text: "On (no default)" },
|
||||
{ key: TtlType.On, text: "On" },
|
||||
{ key: TtlType.Off, text: t(Keys.controls.settings.subSettings.ttlOff), disabled: true },
|
||||
{ key: TtlType.OnNoDefault, text: t(Keys.controls.settings.subSettings.ttlOnNoDefault) },
|
||||
{ key: TtlType.On, text: t(Keys.controls.settings.subSettings.ttlOn) },
|
||||
];
|
||||
|
||||
private getAnalyticalStorageTtlComponent = (): JSX.Element => (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<ChoiceGroup
|
||||
id="analyticalStorageTimeToLive"
|
||||
label="Analytical Storage Time to Live"
|
||||
label={t(Keys.controls.settings.subSettings.analyticalStorageTtl)}
|
||||
selectedKey={this.props.analyticalStorageTtlSelection}
|
||||
options={this.analyticalTtlChoiceGroupOptions}
|
||||
onChange={this.onAnalyticalStorageTtlSelectionChange}
|
||||
@@ -294,7 +303,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
min={1}
|
||||
max={Int32.Max}
|
||||
value={this.props.analyticalStorageTtlSeconds?.toString()}
|
||||
suffix="second(s)"
|
||||
suffix={t(Keys.controls.settings.subSettings.seconds)}
|
||||
onChange={this.onAnalyticalStorageTtlSecondsChange}
|
||||
/>
|
||||
)}
|
||||
@@ -302,14 +311,22 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
);
|
||||
|
||||
private geoSpatialConfigTypeChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{ key: GeospatialConfigType.Geography, text: "Geography", ariaLabel: "geography-option" },
|
||||
{ key: GeospatialConfigType.Geometry, text: "Geometry", ariaLabel: "geometry-option" },
|
||||
{
|
||||
key: GeospatialConfigType.Geography,
|
||||
text: t(Keys.controls.settings.subSettings.geography),
|
||||
ariaLabel: "geography-option",
|
||||
},
|
||||
{
|
||||
key: GeospatialConfigType.Geometry,
|
||||
text: t(Keys.controls.settings.subSettings.geometry),
|
||||
ariaLabel: "geometry-option",
|
||||
},
|
||||
];
|
||||
|
||||
private getGeoSpatialComponent = (): JSX.Element => (
|
||||
<ChoiceGroup
|
||||
id="geoSpatialConfig"
|
||||
label="Geospatial Configuration"
|
||||
label={t(Keys.controls.settings.subSettings.geospatialConfiguration)}
|
||||
selectedKey={this.props.geospatialConfigType}
|
||||
options={this.geoSpatialConfigTypeChoiceGroupOptions}
|
||||
onChange={this.onGeoSpatialConfigTypeChange}
|
||||
@@ -318,8 +335,8 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
);
|
||||
|
||||
private changeFeedChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{ key: ChangeFeedPolicyState.Off, text: "Off" },
|
||||
{ key: ChangeFeedPolicyState.On, text: "On" },
|
||||
{ key: ChangeFeedPolicyState.Off, text: t(Keys.controls.settings.subSettings.ttlOff) },
|
||||
{ key: ChangeFeedPolicyState.On, text: t(Keys.controls.settings.subSettings.ttlOn) },
|
||||
];
|
||||
|
||||
private getChangeFeedComponent = (): JSX.Element => {
|
||||
@@ -328,7 +345,10 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
return (
|
||||
<Stack>
|
||||
<Label id={labelId}>
|
||||
<ToolTipLabelComponent label="Change feed log retention policy" toolTipElement={changeFeedPolicyToolTip} />
|
||||
<ToolTipLabelComponent
|
||||
label={t(Keys.controls.settings.changeFeed.label)}
|
||||
toolTipElement={changeFeedPolicyToolTip}
|
||||
/>
|
||||
</Label>
|
||||
<ChoiceGroup
|
||||
id="changeFeedPolicy"
|
||||
@@ -354,9 +374,10 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
{this.getPartitionKeyVisible() && (
|
||||
<TooltipHost
|
||||
content={`This ${this.partitionKeyName.toLowerCase()} is used to distribute data across multiple partitions for scalability. The value "${
|
||||
this.partitionKeyValue
|
||||
}" determines how documents are partitioned.`}
|
||||
content={t(Keys.controls.settings.subSettings.partitionKeyTooltipTemplate, {
|
||||
partitionKeyName: this.partitionKeyName.toLowerCase(),
|
||||
partitionKeyValue: this.partitionKeyValue,
|
||||
})}
|
||||
styles={{
|
||||
root: {
|
||||
display: "block",
|
||||
@@ -373,14 +394,20 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
)}
|
||||
|
||||
{userContext.apiType === "SQL" && this.isLargePartitionKeyEnabled() && (
|
||||
<Text className={classNames.hintText}>Large {this.partitionKeyName.toLowerCase()} has been enabled.</Text>
|
||||
<Text className={classNames.hintText}>
|
||||
{t(Keys.controls.settings.subSettings.largePartitionKeyEnabled, {
|
||||
partitionKeyName: this.partitionKeyName.toLowerCase(),
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{userContext.apiType === "SQL" &&
|
||||
(this.isHierarchicalPartitionedContainer() ? (
|
||||
<Text className={classNames.hintText}>Hierarchically partitioned container.</Text>
|
||||
<Text className={classNames.hintText}>{t(Keys.controls.settings.subSettings.hierarchicalPartitioned)}</Text>
|
||||
) : (
|
||||
<Text className={classNames.hintText}>Non-hierarchically partitioned container.</Text>
|
||||
<Text className={classNames.hintText}>
|
||||
{t(Keys.controls.settings.subSettings.nonHierarchicalPartitioned)}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Label, Slider, Stack, TextField, Toggle } from "@fluentui/react";
|
||||
import { ThroughputBucket } from "Contracts/DataModels";
|
||||
import { Keys } from "../../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../../Localization/t";
|
||||
import React, { FC, useEffect, useState } from "react";
|
||||
import { isDirty } from "../../SettingsUtils";
|
||||
|
||||
@@ -65,7 +67,9 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
|
||||
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: "m" }} styles={{ root: { width: "70%", maxWidth: 700 } }}>
|
||||
<Label styles={{ root: { color: "var(--colorNeutralForeground1)" } }}>Throughput Buckets</Label>
|
||||
<Label styles={{ root: { color: "var(--colorNeutralForeground1)" } }}>
|
||||
{t(Keys.controls.settings.throughputBuckets.label)}
|
||||
</Label>
|
||||
<Stack>
|
||||
{throughputBuckets?.map((bucket) => (
|
||||
<Stack key={bucket.id} horizontal tokens={{ childrenGap: 8 }} verticalAlign="center">
|
||||
@@ -76,7 +80,9 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
|
||||
value={bucket.maxThroughputPercentage}
|
||||
onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
|
||||
showValue={false}
|
||||
label={`Bucket ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`}
|
||||
label={`${t(Keys.controls.settings.throughputBuckets.bucketLabel, { id: String(bucket.id) })}${
|
||||
bucket.id === 1 ? t(Keys.controls.settings.throughputBuckets.dataExplorerQueryBucket) : ""
|
||||
}`}
|
||||
styles={{
|
||||
root: { flex: 2, maxWidth: 400 },
|
||||
titleLabel: {
|
||||
@@ -99,8 +105,8 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
|
||||
disabled={bucket.maxThroughputPercentage === 100}
|
||||
/>
|
||||
<Toggle
|
||||
onText="Active"
|
||||
offText="Inactive"
|
||||
onText={t(Keys.controls.settings.throughputBuckets.active)}
|
||||
offText={t(Keys.controls.settings.throughputBuckets.inactive)}
|
||||
checked={bucket.maxThroughputPercentage !== 100}
|
||||
onChange={(event, checked) => onToggle(bucket.id, checked)}
|
||||
styles={{
|
||||
|
||||
@@ -18,6 +18,8 @@ import {
|
||||
} from "@fluentui/react";
|
||||
import React from "react";
|
||||
import * as DataModels from "../../../../../Contracts/DataModels";
|
||||
import { Keys } from "../../../../../Localization/Keys.generated";
|
||||
import { t } from "../../../../../Localization/t";
|
||||
import * as SharedConstants from "../../../../../Shared/Constants";
|
||||
import { Action, ActionModifiers } from "../../../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../../../Shared/Telemetry/TelemetryProcessor";
|
||||
@@ -97,8 +99,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
private throughputInputMaxValue: number;
|
||||
private autoPilotInputMaxValue: number;
|
||||
private options: IChoiceGroupOption[] = [
|
||||
{ key: "true", text: "Autoscale" },
|
||||
{ key: "false", text: "Manual" },
|
||||
{ key: "true", text: t(Keys.controls.settings.throughputInput.autoscale) },
|
||||
{ key: "false", text: t(Keys.controls.settings.throughputInput.manual) },
|
||||
];
|
||||
|
||||
// Style constants for theme-aware colors and layout
|
||||
@@ -244,7 +246,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
return (
|
||||
<div>
|
||||
<Text style={{ fontWeight: 600, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
Updated cost per month
|
||||
{t(Keys.controls.settings.costEstimate.updatedCostPerMonth)}
|
||||
</Text>
|
||||
<Stack horizontal style={{ marginTop: 5, marginBottom: 10 }}>
|
||||
<Text
|
||||
@@ -253,7 +255,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY,
|
||||
}}
|
||||
>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice / 10)} min
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice / 10)}{" "}
|
||||
{t(Keys.controls.settings.throughputInput.min)}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
@@ -261,7 +264,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY,
|
||||
}}
|
||||
>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)} max
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)}{" "}
|
||||
{t(Keys.controls.settings.throughputInput.max)}
|
||||
</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
@@ -274,7 +278,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
<Stack {...checkBoxAndInputStackProps} style={{ marginTop: 15 }}>
|
||||
{newThroughput && newThroughputCostElement()}
|
||||
<Text style={{ fontWeight: 600, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
Current cost per month
|
||||
{t(Keys.controls.settings.costEstimate.currentCostPerMonth)}
|
||||
</Text>
|
||||
<Stack horizontal style={{ marginTop: 5, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
<Text
|
||||
@@ -283,7 +287,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY,
|
||||
}}
|
||||
>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice / 10)} min
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice / 10)}{" "}
|
||||
{t(Keys.controls.settings.throughputInput.min)}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
@@ -291,7 +296,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY,
|
||||
}}
|
||||
>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)} max
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)}{" "}
|
||||
{t(Keys.controls.settings.throughputInput.max)}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -326,7 +332,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
return (
|
||||
<div>
|
||||
<Text style={{ fontWeight: 600, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
Updated cost per month
|
||||
{t(Keys.controls.settings.costEstimate.updatedCostPerMonth)}
|
||||
</Text>
|
||||
<Stack horizontal style={{ marginTop: 5, marginBottom: 10 }}>
|
||||
<Text style={this.settingsAndScaleStyle.root}>
|
||||
@@ -349,7 +355,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
<Stack {...checkBoxAndInputStackProps} style={{ marginTop: 15 }}>
|
||||
{newThroughput && newThroughputCostElement()}
|
||||
<Text style={{ fontWeight: 600, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
Current cost per month
|
||||
{t(Keys.controls.settings.costEstimate.currentCostPerMonth)}
|
||||
</Text>
|
||||
<Stack horizontal style={{ marginTop: 5 }}>
|
||||
<Text style={this.settingsAndScaleStyle.root}>
|
||||
@@ -444,10 +450,14 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
this.setState({ spendAckChecked: checked });
|
||||
|
||||
private getStorageCapacityTitle = (): JSX.Element => {
|
||||
const capacity: string = this.props.isFixed ? "Fixed" : "Unlimited";
|
||||
const capacity: string = this.props.isFixed
|
||||
? t(Keys.controls.settings.throughputInput.fixed)
|
||||
: t(Keys.controls.settings.throughputInput.unlimited);
|
||||
return (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<Label style={{ color: "var(--colorNeutralForeground1)" }}>Storage capacity</Label>
|
||||
<Label style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
{t(Keys.controls.settings.throughputInput.storageCapacity)}
|
||||
</Label>
|
||||
<Text style={{ color: "var(--colorNeutralForeground1)" }}>{capacity}</Text>
|
||||
</Stack>
|
||||
);
|
||||
@@ -555,10 +565,14 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
/>
|
||||
<Stack horizontal>
|
||||
<Stack.Item style={{ width: "34%", paddingRight: "5px" }}>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>Instant</Separator>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>
|
||||
{t(Keys.controls.settings.throughputInput.instant)}
|
||||
</Separator>
|
||||
</Stack.Item>
|
||||
<Stack.Item style={{ width: "66%", paddingLeft: "5px" }}>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>4-6 hrs</Separator>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>
|
||||
{t(Keys.controls.settings.throughputInput.fourToSixHrs)}
|
||||
</Separator>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -638,7 +652,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
variant="small"
|
||||
style={{ lineHeight: "20px", fontWeight: 600, color: "var(--colorNeutralForeground1)" }}
|
||||
>
|
||||
Minimum RU/s
|
||||
{t(Keys.controls.settings.throughputInput.minimumRuS)}
|
||||
</Text>
|
||||
<FontIcon iconName="Info" style={{ fontSize: 12, color: "var(--colorNeutralForeground2)" }} />
|
||||
</Stack>
|
||||
@@ -672,7 +686,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
}}
|
||||
>
|
||||
x 10 =
|
||||
{t(Keys.controls.settings.throughputInput.x10Equals)}
|
||||
</Text>
|
||||
|
||||
{/* Column 3: Maximum RU/s */}
|
||||
@@ -682,7 +696,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
variant="small"
|
||||
style={{ lineHeight: "20px", fontWeight: 600, color: "var(--colorNeutralForeground1)" }}
|
||||
>
|
||||
Maximum RU/s
|
||||
{t(Keys.controls.settings.throughputInput.maximumRuS)}
|
||||
</Text>
|
||||
<FontIcon iconName="Info" style={{ fontSize: 12, color: "var(--colorNeutralForeground2)" }} />
|
||||
</Stack>
|
||||
@@ -723,7 +737,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
onGetErrorMessage={(value: string) => {
|
||||
const sanitizedValue = getSanitizedInputValue(value);
|
||||
const errorMessage: string =
|
||||
sanitizedValue % 1000 ? "Throughput value must be in increments of 1000" : this.props.throughputError;
|
||||
sanitizedValue % 1000
|
||||
? t(Keys.controls.settings.throughput.throughputIncrementError)
|
||||
: this.props.throughputError;
|
||||
return <span data-test="autopilot-throughput-input-error">{errorMessage}</span>;
|
||||
}}
|
||||
validateOnLoad={false}
|
||||
@@ -769,7 +785,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
)}
|
||||
{this.props.isAutoPilotSelected ? (
|
||||
<Text style={{ marginTop: "40px", color: "var(--colorNeutralForeground1)" }}>
|
||||
Based on usage, your {this.props.collectionName ? "container" : "database"} throughput will scale from{" "}
|
||||
{t(Keys.controls.settings.throughputInput.autoscaleDescription, {
|
||||
resourceType: this.props.collectionName ? "container" : "database",
|
||||
})}{" "}
|
||||
<b>
|
||||
{AutoPilotUtils.getMinRUsBasedOnUserInput(this.props.maxAutoPilotThroughput)} RU/s (10% of max RU/s) -{" "}
|
||||
{this.props.maxAutoPilotThroughput} RU/s
|
||||
@@ -784,16 +802,19 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
styles={this.darkThemeMessageBarStyles}
|
||||
style={{ marginTop: "40px" }}
|
||||
>
|
||||
{`Billing will apply if you provision more than ${SharedConstants.FreeTierLimits.RU} RU/s of manual throughput, or if the resource scales beyond ${SharedConstants.FreeTierLimits.RU} RU/s with autoscale.`}
|
||||
{t(Keys.controls.settings.throughputInput.freeTierWarning, {
|
||||
ru: String(SharedConstants.FreeTierLimits.RU),
|
||||
})}
|
||||
</MessageBar>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!this.overrideWithProvisionedThroughputSettings() && (
|
||||
<Text style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
Estimate your required RU/s with
|
||||
{t(Keys.controls.settings.throughputInput.capacityCalculator)}
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/">
|
||||
{` capacity calculator`} <FontIcon iconName="NavigateExternalInline" />
|
||||
{t(Keys.controls.settings.throughputInput.capacityCalculatorLink)}{" "}
|
||||
<FontIcon iconName="NavigateExternalInline" />
|
||||
</Link>
|
||||
</Text>
|
||||
)}
|
||||
@@ -806,9 +827,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
onChange={this.onSpendAckChecked}
|
||||
/>
|
||||
)}
|
||||
{this.props.isFixed && (
|
||||
<p>When using a collection with fixed storage capacity, you can set up to 10,000 RU/s.</p>
|
||||
)}
|
||||
{this.props.isFixed && <p>{t(Keys.controls.settings.throughputInput.fixedStorageNote)}</p>}
|
||||
{this.props.collectionName && (
|
||||
<Stack.Item style={{ marginTop: "40px" }}>{this.getStorageCapacityTitle()}</Stack.Item>
|
||||
)}
|
||||
|
||||
@@ -552,9 +552,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
You are not able to lower throughput below your current minimum of
|
||||
10000
|
||||
RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
You are not able to lower throughput below your current minimum of 10000 RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
<StyledLinkBase
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/concepts-limits#minimum-throughput-limits"
|
||||
target="_blank"
|
||||
@@ -572,9 +570,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
Based on usage, your
|
||||
container
|
||||
throughput will scale from
|
||||
Based on usage, your container throughput will scale from
|
||||
|
||||
<b>
|
||||
400
|
||||
@@ -683,7 +679,8 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
$
|
||||
|
||||
35.04
|
||||
min
|
||||
|
||||
min
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
@@ -696,7 +693,8 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
$
|
||||
|
||||
350.40
|
||||
max
|
||||
|
||||
max
|
||||
</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
@@ -730,7 +728,8 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
$
|
||||
|
||||
35.04
|
||||
min
|
||||
|
||||
min
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
@@ -743,7 +742,8 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
$
|
||||
|
||||
350.40
|
||||
max
|
||||
|
||||
max
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -1151,9 +1151,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
You are not able to lower throughput below your current minimum of
|
||||
10000
|
||||
RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
You are not able to lower throughput below your current minimum of 10000 RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
<StyledLinkBase
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/concepts-limits#minimum-throughput-limits"
|
||||
target="_blank"
|
||||
@@ -1728,9 +1726,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
You are not able to lower throughput below your current minimum of
|
||||
10000
|
||||
RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
You are not able to lower throughput below your current minimum of 10000 RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
<StyledLinkBase
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/concepts-limits#minimum-throughput-limits"
|
||||
target="_blank"
|
||||
|
||||
@@ -27,7 +27,8 @@ exports[`ComputedPropertiesComponent renders 1`] = `
|
||||
iconName="NavigateExternalInline"
|
||||
/>
|
||||
</StyledLinkBase>
|
||||
about how to define computed properties and how to use them.
|
||||
|
||||
about how to define computed properties and how to use them.
|
||||
</Text>
|
||||
<div
|
||||
className="settingsV2Editor"
|
||||
|
||||
@@ -33,8 +33,7 @@ exports[`PartitionKeyComponent renders default component and matches snapshot 1`
|
||||
}
|
||||
}
|
||||
>
|
||||
Change
|
||||
partition key
|
||||
Change partition key
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
@@ -61,8 +60,7 @@ exports[`PartitionKeyComponent renders default component and matches snapshot 1`
|
||||
}
|
||||
}
|
||||
>
|
||||
Current
|
||||
partition key
|
||||
Current partition key
|
||||
</Text>
|
||||
<Text
|
||||
styles={
|
||||
@@ -223,8 +221,7 @@ exports[`PartitionKeyComponent renders read-only component and matches snapshot
|
||||
}
|
||||
}
|
||||
>
|
||||
Current
|
||||
partition key
|
||||
Current partition key
|
||||
</Text>
|
||||
<Text
|
||||
styles={
|
||||
|
||||
@@ -410,9 +410,7 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
@@ -984,9 +982,7 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
@@ -1522,9 +1518,7 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
@@ -2157,9 +2151,7 @@ exports[`SubSettingsComponent renders 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
@@ -2729,9 +2721,7 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
|
||||
Reference in New Issue
Block a user