import { DetailsRow, ICheckboxStyles, IChoiceGroupStyles, IDetailsColumnStyles, IDetailsListStyles, IDetailsRowProps, IDetailsRowStyles, IDropdownStyles, IMessageBarStyles, ISeparatorStyles, IStackProps, IStackStyles, IStackTokens, ITextFieldStyles, ITextStyles, Link, MessageBar, MessageBarType, Spinner, SpinnerSize, Stack, Text, } from "@fluentui/react"; import { Keys, t } from "Localization"; import * as React from "react"; import { Urls } from "../../../Common/Constants"; import { StyleConstants } from "../../../Common/StyleConstants"; import { hoursInAMonth } from "../../../Shared/Constants"; import { computeRUUsagePriceHourly, estimatedCostDisclaimer, getAutoscalePricePerRu, getCurrencySign, getMultimasterMultiplier, getPriceCurrency, getPricePerRu, } from "../../../Utils/PricingUtils"; import { isDirty, isDirtyTypes } from "./SettingsUtils"; export interface EstimatedSpendingDisplayProps { costType: JSX.Element; } export interface ManualEstimatedSpendingDisplayProps extends EstimatedSpendingDisplayProps { hourly: JSX.Element; daily: JSX.Element; monthly: JSX.Element; } export interface AutoscaleEstimatedSpendingDisplayProps extends EstimatedSpendingDisplayProps { minPerMonth: JSX.Element; maxPerMonth: JSX.Element; } export interface PriceBreakdown { hourlyPrice: number; dailyPrice: number; monthlyPrice: number; pricePerRu: number; currency: string; currencySign: string; } export type editorType = "indexPolicy" | "computedProperties" | "dataMasking"; export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14, color: "var(--colorNeutralForeground1)" } }; export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = { label: { margin: 0, padding: "2 0 2 0", }, text: { fontSize: 12, }, }; export const subComponentStackProps: Partial = { tokens: { childrenGap: 20 }, }; export const titleAndInputStackProps: Partial = { tokens: { childrenGap: 5 }, }; export const mongoWarningStackProps: Partial = { tokens: { childrenGap: 5 }, }; export const mongoErrorMessageStyles: Partial = { root: { marginLeft: 10 } }; export const createAndAddMongoIndexStackProps: Partial = { tokens: { childrenGap: 5 }, }; export const addMongoIndexStackProps: Partial = { tokens: { childrenGap: 10 }, }; export const checkBoxAndInputStackProps: Partial = { tokens: { childrenGap: 10 }, }; export const relaxedSpacingStackProps: Partial = { tokens: { childrenGap: 20 }, }; export const toolTipLabelStackTokens: IStackTokens = { childrenGap: 6, }; export const accordionStackTokens: IStackTokens = { childrenGap: 10, }; export const addMongoIndexSubElementsTokens: IStackTokens = { childrenGap: 20, }; export const mediumWidthStackStyles: IStackStyles = { root: { width: 600 } }; export const shortWidthTextFieldStyles: Partial = { root: { paddingLeft: 10, width: 210 }, fieldGroup: { backgroundColor: "var(--colorNeutralBackground2)", borderColor: "var(--colorNeutralStroke1)", }, field: { color: "var(--colorNeutralForeground1)", backgroundColor: "var(--colorNeutralBackground2)", }, }; export const shortWidthDropDownStyles: Partial = { dropdown: { paddingLeft: 10, width: 202 }, title: { backgroundColor: "var(--colorNeutralBackground2)", color: "var(--colorNeutralForeground1)", borderColor: "var(--colorNeutralStroke1)", }, caretDown: { color: "var(--colorNeutralForeground1)", }, callout: { backgroundColor: "var(--colorNeutralBackground2)", border: "1px solid var(--colorNeutralStroke1)", }, dropdownItems: { backgroundColor: "var(--colorNeutralBackground2)", }, dropdownItem: { backgroundColor: "transparent", color: "var(--colorNeutralForeground1)", selectors: { "&:hover": { backgroundColor: "rgba(255, 255, 255, 0.1)", color: "var(--colorNeutralForeground1)", }, "&:focus": { backgroundColor: "rgba(255, 255, 255, 0.1)", color: "var(--colorNeutralForeground1)", }, }, }, dropdownItemSelected: { backgroundColor: "rgba(255, 255, 255, 0.08)", color: "var(--colorNeutralForeground1)", selectors: { "&:hover": { backgroundColor: "rgba(255, 255, 255, 0.1)", color: "var(--colorNeutralForeground1)", }, }, }, dropdownOptionText: { color: "var(--colorNeutralForeground1)", }, }; export const transparentDetailsRowStyles: Partial = { root: { backgroundColor: "var(--colorNeutralBackground1)", color: "var(--colorNeutralForeground1)", selectors: { ":hover": { backgroundColor: "var(--colorNeutralBackground1Hover)", color: "var(--colorNeutralForeground1)", }, ":hover .ms-DetailsRow-cell": { backgroundColor: "var(--colorNeutralBackground1Hover)", color: "var(--colorNeutralForeground1)", }, "&.ms-DetailsRow": { backgroundColor: "var(--colorNeutralBackground1)", }, }, }, cell: { backgroundColor: "var(--colorNeutralBackground1)", color: "var(--colorNeutralForeground1)", selectors: { ":hover": { backgroundColor: "var(--colorNeutralBackground1Hover)", color: "var(--colorNeutralForeground1)", }, }, }, }; export const transparentDetailsHeaderStyle: Partial = { root: { color: "var(--colorNeutralForeground1)", selectors: { ":hover": { background: "var(--colorNeutralBackground1Hover)", color: "var(--colorNeutralForeground1)", }, }, }, }; export const customDetailsListStyles: Partial = { root: { selectors: { ".ms-FocusZone": { paddingTop: 0, }, ".ms-DetailsHeader": { backgroundColor: "var(--colorNeutralBackground1)", }, ".ms-DetailsHeader-cell": { color: "var(--colorNeutralForeground1)", backgroundColor: "var(--colorNeutralBackground1)", selectors: { ":hover": { backgroundColor: "var(--colorNeutralBackground1Hover)", color: "var(--colorNeutralForeground1)", }, }, }, ".ms-DetailsHeader-cellTitle": { color: "var(--colorNeutralForeground1)", }, ".ms-DetailsRow": { color: "var(--colorNeutralForeground1)", }, ".ms-DetailsRow-cell": { color: "var(--colorNeutralForeground1)", }, // Tooltip styling for cells ".ms-TooltipHost": { color: "var(--colorNeutralForeground1)", }, ".ms-DetailsRow-cell .ms-TooltipHost": { color: "var(--colorNeutralForeground1)", }, }, }, }; export const separatorStyles: Partial = { root: [ { selectors: { "::before": { background: StyleConstants.BaseMedium, }, }, }, ], }; export const messageBarStyles: Partial = { root: { marginTop: "5px", backgroundColor: "var(--colorNeutralBackground1)", selectors: { "&.ms-MessageBar--severeWarning": { backgroundColor: "var(--colorNeutralBackground4)", }, "&.ms-MessageBar--warning": { backgroundColor: "var(--colorNeutralBackground3)", }, }, }, text: { fontSize: 14 }, }; export const unsavedEditorMessageBarStyles: Partial = { root: { marginTop: "5px", padding: "8px 12px", }, text: { fontSize: 14 }, }; export const throughputUnit = "RU/s"; export function onRenderRow(props: IDetailsRowProps): JSX.Element { return ; } export const getRuPriceBreakdown = ( throughput: number, serverId: string, numberOfRegions: number, isMultimaster: boolean, isAutoscale: boolean, ): PriceBreakdown => { const hourlyPrice: number = computeRUUsagePriceHourly({ serverId: serverId, requestUnits: throughput, numberOfRegions: numberOfRegions, multimasterEnabled: isMultimaster, isAutoscale: isAutoscale, }); const multimasterMultiplier = getMultimasterMultiplier(numberOfRegions, isMultimaster); const pricePerRu: number = isAutoscale ? getAutoscalePricePerRu(serverId, multimasterMultiplier) : getPricePerRu(serverId, multimasterMultiplier); return { hourlyPrice, dailyPrice: hourlyPrice * 24, monthlyPrice: hourlyPrice * hoursInAMonth, pricePerRu, currency: getPriceCurrency(serverId), currencySign: getCurrencySign(serverId), }; }; export const getEstimatedSpendingElement = ( costElement: JSX.Element, throughput: number, numberOfRegions: number, priceBreakdown: PriceBreakdown, isAutoscale: boolean, ): JSX.Element => { const ruRange: string = isAutoscale ? throughput / 10 + " RU/s - " : ""; return ( {t(Keys.controls.settings.costEstimate.title)} {costElement} {t(Keys.controls.settings.costEstimate.howWeCalculate)} {numberOfRegions} region{numberOfRegions > 1 && s} {ruRange} {throughput} RU/s {priceBreakdown.currencySign} {priceBreakdown.pricePerRu} {t(Keys.controls.settings.costEstimate.perRu)} *{estimatedCostDisclaimer} ); }; export const manualToAutoscaleDisclaimerElement: JSX.Element = ( {t(Keys.controls.settings.throughput.manualToAutoscaleDisclaimer)}{" "} {t(Keys.common.learnMore)} ); export const ttlWarning: JSX.Element = ( {t(Keys.controls.settings.throughput.ttlWarningText)}{" "} {t(Keys.controls.settings.throughput.ttlWarningLinkText)} . ); export const unsavedEditorWarningMessage = (editor: editorType): JSX.Element => ( {t(Keys.controls.settings.throughput.unsavedEditorWarningPrefix)}{" "} {editor === "indexPolicy" ? t(Keys.controls.settings.throughput.unsavedIndexingPolicy) : editor === "dataMasking" ? t(Keys.controls.settings.throughput.unsavedDataMaskingPolicy) : t(Keys.controls.settings.throughput.unsavedComputedProperties)} {t(Keys.controls.settings.throughput.unsavedEditorWarningSuffix)} ); export const updateThroughputDelayedApplyWarningMessage: JSX.Element = ( {t(Keys.controls.settings.throughput.updateDelayedApplyWarning)} ); export const getUpdateThroughputBeyondInstantLimitMessage = (instantMaximumThroughput: number): JSX.Element => { return ( {t(Keys.controls.settings.throughput.scalingUpDelayMessage, { instantMaximumThroughput: String(instantMaximumThroughput), })} ); }; export const getUpdateThroughputBeyondSupportLimitMessage = ( instantMaximumThroughput: number, maximumThroughput: number, ): JSX.Element => { return ( <> {t(Keys.controls.settings.throughput.exceedPreAllocatedMessage)}
  1. {t(Keys.controls.settings.throughput.instantScaleOption, { instantMaximumThroughput: String(instantMaximumThroughput), })}
  2. {instantMaximumThroughput < maximumThroughput && (
  3. {t(Keys.controls.settings.throughput.asyncScaleOption, { maximumThroughput: String(maximumThroughput) })}
  4. )}
  5. {t(Keys.controls.settings.throughput.quotaMaxOption, { maximumThroughput: String(maximumThroughput) })} {t(Keys.common.learnMore)}
); }; export const getUpdateThroughputBelowMinimumMessage = (minimum: number): JSX.Element => { return ( {t(Keys.controls.settings.throughput.belowMinimumMessage, { minimum: String(minimum) })} {t(Keys.common.learnMore)} ); }; export const saveThroughputWarningMessage: JSX.Element = ( {t(Keys.controls.settings.throughput.saveThroughputWarning)} ); const getCurrentThroughput = ( isAutoscale: boolean, throughput: number, throughputUnit: string, targetThroughput?: number, ): string => { if (targetThroughput) { if (throughput) { return isAutoscale ? `, ${t(Keys.controls.settings.throughput.currentAutoscaleThroughput)} ${Math.round( throughput / 10, )} - ${throughput} ${throughputUnit}, ${t( Keys.controls.settings.throughput.targetAutoscaleThroughput, )} ${Math.round(targetThroughput / 10)} - ${targetThroughput} ${throughputUnit}` : `, ${t(Keys.controls.settings.throughput.currentManualThroughput)} ${throughput} ${throughputUnit}, ${t( Keys.controls.settings.throughput.targetManualThroughput, )} ${targetThroughput}`; } else { return isAutoscale ? `, ${t(Keys.controls.settings.throughput.targetAutoscaleThroughput)} ${Math.round( targetThroughput / 10, )} - ${targetThroughput} ${throughputUnit}` : `, ${t(Keys.controls.settings.throughput.targetManualThroughput)} ${targetThroughput} ${throughputUnit}`; } } if (!targetThroughput && throughput) { return isAutoscale ? `, ${t(Keys.controls.settings.throughput.currentAutoscaleThroughput)} ${Math.round( throughput / 10, )} - ${throughput} ${throughputUnit}` : `, ${t(Keys.controls.settings.throughput.currentManualThroughput)} ${throughput} ${throughputUnit}`; } return ""; }; export const getThroughputApplyDelayedMessage = ( isAutoscale: boolean, throughput: number, throughputUnit: string, databaseName: string, collectionName: string, requestedThroughput: number, ): JSX.Element => ( {t(Keys.controls.settings.throughput.applyDelayedMessage)}
{t(Keys.controls.settings.throughput.databaseLabel)} {databaseName},{" "} {t(Keys.controls.settings.throughput.containerLabel)} {collectionName}{" "} {getCurrentThroughput(isAutoscale, throughput, throughputUnit, requestedThroughput)}
); export const getThroughputApplyShortDelayMessage = ( isAutoscale: boolean, throughput: number, throughputUnit: string, databaseName: string, collectionName: string, ): JSX.Element => ( {t(Keys.controls.settings.throughput.applyShortDelayMessage)}
{collectionName ? `${t(Keys.controls.settings.throughput.databaseLabel)} ${databaseName}, ${t( Keys.controls.settings.throughput.containerLabel, )} ${collectionName} ` : `${t(Keys.controls.settings.throughput.databaseLabel)} ${databaseName} `} {getCurrentThroughput(isAutoscale, throughput, throughputUnit)}
); export const getThroughputApplyLongDelayMessage = ( isAutoscale: boolean, throughput: number, throughputUnit: string, databaseName: string, collectionName: string, requestedThroughput: number, ): JSX.Element => ( {t(Keys.controls.settings.throughput.applyLongDelayMessage)}
{collectionName ? `${t(Keys.controls.settings.throughput.databaseLabel)} ${databaseName}, ${t( Keys.controls.settings.throughput.containerLabel, )} ${collectionName} ` : `${t(Keys.controls.settings.throughput.databaseLabel)} ${databaseName} `} {getCurrentThroughput(isAutoscale, throughput, throughputUnit, requestedThroughput)}
); export const getToolTipContainer = (content: string | JSX.Element): JSX.Element => content ? {content} : undefined; export const conflictResolutionLwwTooltip: JSX.Element = ( {t(Keys.controls.settings.conflictResolution.lwwTooltip)} ); export const conflictResolutionCustomToolTip: JSX.Element = ( {t(Keys.controls.settings.conflictResolution.customTooltip)} {t(Keys.controls.settings.conflictResolution.customTooltipConflictsFeed)} {t(Keys.controls.settings.conflictResolution.customTooltipSuffix)} ); export const changeFeedPolicyToolTip: JSX.Element = ( {t(Keys.controls.settings.changeFeed.tooltip)} ); export const mongoIndexingPolicyDisclaimer: JSX.Element = ( {t(Keys.controls.settings.mongoIndexing.disclaimer)} {t(Keys.controls.settings.mongoIndexing.disclaimerCompoundIndexesLink)} {t(Keys.controls.settings.mongoIndexing.disclaimerSuffix)} ); export const mongoCompoundIndexNotSupportedMessage: JSX.Element = ( {t(Keys.controls.settings.mongoIndexing.compoundNotSupported)} ); export const mongoIndexingPolicyAADError: JSX.Element = ( {t(Keys.controls.settings.mongoIndexing.aadError)} {t(Keys.controls.settings.mongoIndexing.aadErrorLink)} ); export const mongoIndexTransformationRefreshingMessage: JSX.Element = ( {t(Keys.controls.settings.mongoIndexing.refreshingProgress)} ); export const renderMongoIndexTransformationRefreshMessage = ( progress: number, performRefresh: () => void, ): JSX.Element => { if (progress === 0) { return ( {t(Keys.controls.settings.mongoIndexing.canMakeMoreChangesZero)} {t(Keys.controls.settings.mongoIndexing.refreshToCheck)} ); } else { return ( {`${t(Keys.controls.settings.mongoIndexing.canMakeMoreChangesProgress).replace( "{{progress}}", String(progress), )} `} {t(Keys.controls.settings.mongoIndexing.refreshToCheckProgress)} ); } }; export const getTextFieldStyles = (current: isDirtyTypes, baseline: isDirtyTypes): Partial => ({ fieldGroup: { height: 25, width: 300, backgroundColor: "var(--colorNeutralBackground2)", borderColor: isDirty(current, baseline) ? StyleConstants.Dirty : "var(--colorNeutralStroke1)", selectors: { ":disabled": { backgroundColor: "var(--colorNeutralBackground2)", borderColor: "var(--colorNeutralStroke1)", color: "var(--colorNeutralForeground2)", }, input: { backgroundColor: "var(--colorNeutralBackground2)", color: "var(--colorNeutralForeground1)", }, "input:disabled": { backgroundColor: "var(--colorNeutralBackground2)", color: "var(--colorNeutralForeground2)", }, "input#autopilotInput": { backgroundColor: "var(--colorNeutralBackground4)", color: "var(--colorNeutralForeground1)", }, }, }, field: { backgroundColor: "var(--colorNeutralBackground2)", color: "var(--colorNeutralForeground1)", selectors: { ":disabled": { backgroundColor: "var(--colorNeutralBackground2)", color: "var(--colorNeutralForeground2)", }, }, }, subComponentStyles: { label: { root: { color: "var(--colorNeutralForeground1)", }, }, }, suffix: { backgroundColor: "var(--colorNeutralBackground2)", color: "var(--colorNeutralForeground1)", border: "1px solid var(--colorNeutralStroke1)", }, }); export const getChoiceGroupStyles = ( current: isDirtyTypes, baseline: isDirtyTypes, isHorizontal?: boolean, ): Partial => ({ label: { color: "var(--colorNeutralForeground1)", }, root: { selectors: { ".ms-ChoiceFieldLabel": { color: "var(--colorNeutralForeground1)", }, ".ms-ChoiceField-field:hover .ms-ChoiceFieldLabel": { color: "var(--colorNeutralForeground1)", }, ".ms-ChoiceField:hover .ms-ChoiceFieldLabel": { color: "var(--colorNeutralForeground1)", }, ".ms-ChoiceField:hover .ms-ChoiceField-innerField": { color: "var(--colorNeutralForeground1)", }, ".ms-ChoiceField-innerField": { color: "var(--colorNeutralForeground1)", }, }, }, flexContainer: [ { selectors: { ".ms-ChoiceField-field.is-checked::before": { borderColor: isDirty(current, baseline) ? StyleConstants.Dirty : "", }, ".ms-ChoiceField-field.is-checked::after": { borderColor: isDirty(current, baseline) ? StyleConstants.Dirty : "", }, ".ms-ChoiceField-wrapper label": { whiteSpace: "nowrap", fontSize: 14, fontFamily: StyleConstants.DataExplorerFont, padding: "2px 5px", color: "var(--colorNeutralForeground1)", }, ".ms-ChoiceFieldLabel": { color: "var(--colorNeutralForeground1)", }, ".ms-ChoiceFieldLabel:hover": { color: "var(--colorNeutralForeground1)", }, ".ms-ChoiceField-field:hover .ms-ChoiceFieldLabel": { color: "var(--colorNeutralForeground1)", }, }, display: isHorizontal ? "inline-flex" : "default", columnGap: isHorizontal ? "30px" : "default", }, ], });