mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-18 16:31:31 +00:00
* First dark theme commits for command bar * Updated theme on sidebar * Updated tabs, sidebar, splash screen * settings theme changes * Dark theme applied to Monaco editor * Dark theme to stored procedures * Fixed sidebar scroll * Updated scroll issue in sidebar * Command bar items fixed * Fixed lint errors * fixed lint errors * settings side panel fixed * Second last iteration for css * Fixed all the issues of css * Updated the theme icon for now on DE to change the theme from portal/DE itself * Formatting issue resolved * Remove CloudShellTerminalComponent changes - revert to master version * Fixed test issue * Fixed formatting issue * Fix tests: update snapshots and revert xterm imports for compatibility * Fix xterm imports in CloudShellTerminalComponent to use @xterm packages * Fix Cloud Shell component imports for compatibility * Update test snapshots * Fix xterm package consistency across all CloudShell components * Fix TypeScript compilation errors in CloudShell components and query Documents - Standardized xterm package imports across CloudShell components to use legacy 'xterm' package - Fixed Terminal type compatibility issues in CommonUtils.tsx - Added type casting for enableQueryControl property in queryDocuments.ts to handle Azure Cosmos SDK interface limitations - Applied code formatting to ensure consistency * Update failing snapshot tests - Updated TreeNodeComponent snapshot tests for loading states - Updated ThroughputInputAutoPilotV3Component snapshots for number formatting changes (10,00,000 -> 1,000,000) - All snapshot tests now pass * Fixed test issue * Fixed test issue * Updated the buttons for theme * Updated the Theme changes based on portal theme changes * Updated review comments * Updated the duplicate code and fixed the fabric react error * Few places styling added and resolving few comments * Fixed errors * Fixed comments * Fixed comments * Fixed comments * Fixed full text policy issue for mongoru accounts * Resolved comments for class Name and few others * Added css for homepage in ru accounts * Final commit with all the feedback issues resolved * Lint error resolved * Updated the review comments and few Ui issues * Resolved review comments and changed header bg and active state color * Moved svg code to different file and imported * css fixed for the hpome page boxed for ru account * Lint errors * Fixed boxes issue in ru accounts * Handled the initial theme from the portal * Updated snap * Update snapshots for TreeNodeComponent and CreateCopyJobScreensProvider tests * Fix duplicate DataExplorerRoot test id causing Playwright strict mode violation * Fix locale-dependent number formatting in ThroughputInputAutoPilotV3Component --------- Co-authored-by: Sakshi Gupta <sakshig+microsoft@microsoft.com> Co-authored-by: Sakshi Gupta <sakshig@microsoft.com>
353 lines
14 KiB
TypeScript
353 lines
14 KiB
TypeScript
import { IconButton, ITextFieldStyles, Pivot, PivotItem, PrimaryButton, Stack, Text, TextField } from "@fluentui/react";
|
|
import { handleError } from "Common/ErrorHandlingUtils";
|
|
import { sendMessage } from "Common/MessageHandler";
|
|
import { MessageTypes } from "Contracts/ExplorerContracts";
|
|
import React, { useEffect, useState } from "react";
|
|
import { userContext } from "UserContext";
|
|
import { listKeys, listReadOnlyKeys } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
|
import {
|
|
DatabaseAccountListKeysResult,
|
|
DatabaseAccountListReadOnlyKeysResult,
|
|
} from "Utils/arm/generatedClients/cosmos/types";
|
|
|
|
export const ConnectTab: React.FC = (): JSX.Element => {
|
|
const [primaryMasterKey, setPrimaryMasterKey] = useState<string>("");
|
|
const [secondaryMasterKey, setSecondaryMasterKey] = useState<string>("");
|
|
const [primaryReadonlyMasterKey, setPrimaryReadonlyMasterKey] = useState<string>("");
|
|
const [secondaryReadonlyMasterKey, setSecondaryReadonlyMasterKey] = useState<string>("");
|
|
const uri: string = userContext.databaseAccount.properties?.documentEndpoint;
|
|
const primaryConnectionStr = `AccountEndpoint=${uri};AccountKey=${primaryMasterKey};`;
|
|
const secondaryConnectionStr = `AccountEndpoint=${uri};AccountKey=${secondaryMasterKey};`;
|
|
const primaryReadonlyConnectionStr = `AccountEndpoint=${uri};AccountKey=${primaryReadonlyMasterKey};`;
|
|
const secondaryReadonlyConnectionStr = `AccountEndpoint=${uri};AccountKey=${secondaryReadonlyMasterKey};`;
|
|
const maskedValue: string =
|
|
"*********************************************************************************************************************************";
|
|
const [showPrimaryMasterKey, setShowPrimaryMasterKey] = useState<boolean>(false);
|
|
const [showSecondaryMasterKey, setShowSecondaryMasterKey] = useState<boolean>(false);
|
|
const [showPrimaryReadonlyMasterKey, setShowPrimaryReadonlyMasterKey] = useState<boolean>(false);
|
|
const [showSecondaryReadonlyMasterKey, setShowSecondaryReadonlyMasterKey] = useState<boolean>(false);
|
|
const [showPrimaryConnectionStr, setShowPrimaryConnectionStr] = useState<boolean>(false);
|
|
const [showSecondaryConnectionStr, setShowSecondaryConnectionStr] = useState<boolean>(false);
|
|
const [showPrimaryReadonlyConnectionStr, setShowPrimaryReadonlyConnectionStr] = useState<boolean>(false);
|
|
const [showSecondaryReadonlyConnectionStr, setShowSecondaryReadonlyConnectionStr] = useState<boolean>(false);
|
|
|
|
useEffect(() => {
|
|
fetchKeys();
|
|
}, []);
|
|
|
|
const fetchKeys = async (): Promise<void> => {
|
|
try {
|
|
if (userContext.hasWriteAccess) {
|
|
const listKeysResult: DatabaseAccountListKeysResult = await listKeys(
|
|
userContext.subscriptionId,
|
|
userContext.resourceGroup,
|
|
userContext.databaseAccount.name,
|
|
);
|
|
setPrimaryMasterKey(listKeysResult.primaryMasterKey);
|
|
setSecondaryMasterKey(listKeysResult.secondaryMasterKey);
|
|
setPrimaryReadonlyMasterKey(listKeysResult.primaryReadonlyMasterKey);
|
|
setSecondaryReadonlyMasterKey(listKeysResult.secondaryReadonlyMasterKey);
|
|
} else {
|
|
const listReadonlyKeysResult: DatabaseAccountListReadOnlyKeysResult = await listReadOnlyKeys(
|
|
userContext.subscriptionId,
|
|
userContext.resourceGroup,
|
|
userContext.databaseAccount.name,
|
|
);
|
|
setPrimaryReadonlyMasterKey(listReadonlyKeysResult.primaryReadonlyMasterKey);
|
|
setSecondaryReadonlyMasterKey(listReadonlyKeysResult.secondaryReadonlyMasterKey);
|
|
}
|
|
} catch (error) {
|
|
handleError(error, "listKeys", "listKeys request has failed: ");
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const onCopyBtnClicked = (selector: string): void => {
|
|
const textfield: HTMLInputElement = document.querySelector(selector);
|
|
textfield.select();
|
|
document.execCommand("copy");
|
|
};
|
|
|
|
const textfieldStyles: Partial<ITextFieldStyles> = {
|
|
root: { width: "100%" },
|
|
field: {
|
|
backgroundColor: "var(--colorNeutralBackground3)",
|
|
color: "var(--colorNeutralForeground1)",
|
|
},
|
|
fieldGroup: { borderColor: "var(--colorNeutralStroke1)" },
|
|
suffix: {
|
|
backgroundColor: "var(--colorNeutralBackground3)",
|
|
margin: 0,
|
|
padding: 0,
|
|
},
|
|
subComponentStyles: {
|
|
label: {
|
|
root: {
|
|
color: "var(--colorNeutralForeground1)",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const renderCopyButton = (selector: string) => (
|
|
<IconButton
|
|
iconProps={{ iconName: "Copy" }}
|
|
onClick={() => onCopyBtnClicked(selector)}
|
|
styles={{
|
|
root: {
|
|
height: "100%",
|
|
backgroundColor: "var(--colorNeutralBackground3)",
|
|
border: "none",
|
|
color: "var(--colorNeutralForeground1)",
|
|
},
|
|
rootHovered: {
|
|
backgroundColor: "var(--colorNeutralBackground4)",
|
|
},
|
|
rootPressed: {
|
|
backgroundColor: "var(--colorNeutralBackground5)",
|
|
},
|
|
icon: {
|
|
color: "var(--colorNeutralForeground1)",
|
|
},
|
|
}}
|
|
/>
|
|
);
|
|
|
|
const themeAwareIconButtonStyles = {
|
|
root: {
|
|
color: "var(--colorNeutralForeground1)",
|
|
},
|
|
rootHovered: {
|
|
backgroundColor: "var(--colorNeutralBackground3)",
|
|
},
|
|
icon: {
|
|
color: "var(--colorNeutralForeground1)",
|
|
},
|
|
};
|
|
|
|
const pivotStyles = {
|
|
root: {
|
|
color: "var(--colorNeutralForeground1)",
|
|
},
|
|
link: {
|
|
color: "var(--colorNeutralForeground1)",
|
|
backgroundColor: "transparent",
|
|
selectors: {
|
|
"&:hover": {
|
|
color: "var(--colorNeutralForeground1)",
|
|
backgroundColor: "var(--colorNeutralBackground3)",
|
|
},
|
|
"&:active": {
|
|
color: "var(--colorNeutralForeground1)",
|
|
},
|
|
},
|
|
},
|
|
linkIsSelected: {
|
|
color: "var(--colorNeutralForeground1)",
|
|
selectors: {
|
|
"&::before": {
|
|
backgroundColor: "var(--colorBrandBackground)",
|
|
},
|
|
},
|
|
},
|
|
text: {
|
|
color: "var(--colorNeutralForeground1)",
|
|
},
|
|
};
|
|
|
|
return (
|
|
<div style={{ width: "100%", padding: 16 }}>
|
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 16, margin: 10 }}>
|
|
<TextField
|
|
label="URI"
|
|
id="uriTextfield"
|
|
readOnly
|
|
value={uri}
|
|
styles={textfieldStyles}
|
|
onRenderSuffix={() => renderCopyButton("#uriTextfield")}
|
|
/>
|
|
<div style={{ width: 32 }}></div>
|
|
</Stack>
|
|
|
|
<Pivot styles={pivotStyles}>
|
|
{userContext.hasWriteAccess && (
|
|
<PivotItem headerText="Read-write Keys">
|
|
<Stack style={{ margin: 10, overflow: "auto", maxHeight: "calc(100vh - 300px)" }}>
|
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
|
<TextField
|
|
label="PRIMARY KEY"
|
|
id="primaryKeyTextfield"
|
|
readOnly
|
|
value={showPrimaryMasterKey ? primaryMasterKey : maskedValue}
|
|
styles={textfieldStyles}
|
|
{...(showPrimaryMasterKey && {
|
|
onRenderSuffix: () => renderCopyButton("#primaryKeyTextfield"),
|
|
})}
|
|
/>
|
|
<IconButton
|
|
iconProps={{ iconName: showPrimaryMasterKey ? "Hide3" : "View" }}
|
|
onClick={() => setShowPrimaryMasterKey(!showPrimaryMasterKey)}
|
|
styles={themeAwareIconButtonStyles}
|
|
/>
|
|
</Stack>
|
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
|
<TextField
|
|
label="SECONDARY KEY"
|
|
id="secondaryKeyTextfield"
|
|
readOnly
|
|
value={showSecondaryMasterKey ? secondaryMasterKey : maskedValue}
|
|
styles={textfieldStyles}
|
|
{...(showSecondaryMasterKey && {
|
|
onRenderSuffix: () => renderCopyButton("#secondaryKeyTextfield"),
|
|
})}
|
|
/>
|
|
<IconButton
|
|
iconProps={{ iconName: showSecondaryMasterKey ? "Hide3" : "View" }}
|
|
onClick={() => setShowSecondaryMasterKey(!showSecondaryMasterKey)}
|
|
styles={themeAwareIconButtonStyles}
|
|
/>
|
|
</Stack>
|
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
|
<TextField
|
|
label="PRIMARY CONNECTION STRING"
|
|
id="primaryConStrTextfield"
|
|
readOnly
|
|
value={showPrimaryConnectionStr ? primaryConnectionStr : maskedValue}
|
|
styles={textfieldStyles}
|
|
{...(showPrimaryConnectionStr && {
|
|
onRenderSuffix: () => renderCopyButton("#primaryConStrTextfield"),
|
|
})}
|
|
/>
|
|
<IconButton
|
|
iconProps={{ iconName: showPrimaryConnectionStr ? "Hide3" : "View" }}
|
|
onClick={() => setShowPrimaryConnectionStr(!showPrimaryConnectionStr)}
|
|
styles={themeAwareIconButtonStyles}
|
|
/>
|
|
</Stack>
|
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
|
<TextField
|
|
label="SECONDARY CONNECTION STRING"
|
|
id="secondaryConStrTextfield"
|
|
readOnly
|
|
value={showSecondaryConnectionStr ? secondaryConnectionStr : maskedValue}
|
|
styles={textfieldStyles}
|
|
{...(showSecondaryConnectionStr && {
|
|
onRenderSuffix: () => renderCopyButton("#secondaryConStrTextfield"),
|
|
})}
|
|
/>
|
|
<IconButton
|
|
iconProps={{ iconName: showSecondaryConnectionStr ? "Hide3" : "View" }}
|
|
onClick={() => setShowSecondaryConnectionStr(!showSecondaryConnectionStr)}
|
|
styles={themeAwareIconButtonStyles}
|
|
/>
|
|
</Stack>
|
|
</Stack>
|
|
</PivotItem>
|
|
)}
|
|
<PivotItem headerText="Read-only Keys">
|
|
<Stack style={{ margin: 10, overflow: "auto", maxHeight: "calc(100vh - 300px)" }}>
|
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
|
<TextField
|
|
label="PRIMARY READ-ONLY KEY"
|
|
id="primaryReadonlyKeyTextfield"
|
|
readOnly
|
|
value={showPrimaryReadonlyMasterKey ? primaryReadonlyMasterKey : maskedValue}
|
|
styles={textfieldStyles}
|
|
{...(showPrimaryReadonlyMasterKey && {
|
|
onRenderSuffix: () => renderCopyButton("#primaryReadonlyKeyTextfield"),
|
|
})}
|
|
/>
|
|
<IconButton
|
|
iconProps={{ iconName: showPrimaryReadonlyMasterKey ? "Hide3" : "View" }}
|
|
onClick={() => setShowPrimaryReadonlyMasterKey(!showPrimaryReadonlyMasterKey)}
|
|
styles={themeAwareIconButtonStyles}
|
|
/>
|
|
</Stack>
|
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
|
<TextField
|
|
label="SECONDARY READ-ONLY KEY"
|
|
id="secondaryReadonlyKeyTextfield"
|
|
readOnly
|
|
value={showSecondaryReadonlyMasterKey ? secondaryReadonlyMasterKey : maskedValue}
|
|
styles={textfieldStyles}
|
|
{...(showSecondaryReadonlyMasterKey && {
|
|
onRenderSuffix: () => renderCopyButton("#secondaryReadonlyKeyTextfield"),
|
|
})}
|
|
/>
|
|
<IconButton
|
|
iconProps={{ iconName: showSecondaryReadonlyMasterKey ? "Hide3" : "View" }}
|
|
onClick={() => setShowSecondaryReadonlyMasterKey(!showSecondaryReadonlyMasterKey)}
|
|
styles={themeAwareIconButtonStyles}
|
|
/>
|
|
</Stack>
|
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
|
<TextField
|
|
label="PRIMARY READ-ONLY CONNECTION STRING"
|
|
id="primaryReadonlyConStrTextfield"
|
|
readOnly
|
|
value={showPrimaryReadonlyConnectionStr ? primaryReadonlyConnectionStr : maskedValue}
|
|
styles={textfieldStyles}
|
|
{...(showPrimaryReadonlyConnectionStr && {
|
|
onRenderSuffix: () => renderCopyButton("#primaryReadonlyConStrTextfield"),
|
|
})}
|
|
/>
|
|
<IconButton
|
|
iconProps={{ iconName: showPrimaryReadonlyConnectionStr ? "Hide3" : "View" }}
|
|
onClick={() => setShowPrimaryReadonlyConnectionStr(!showPrimaryReadonlyConnectionStr)}
|
|
styles={themeAwareIconButtonStyles}
|
|
/>
|
|
</Stack>
|
|
<Stack horizontal verticalAlign="end" style={{ marginBottom: 8 }}>
|
|
<TextField
|
|
label="SECONDARY READ-ONLY CONNECTION STRING"
|
|
id="secondaryReadonlyConStrTextfield"
|
|
value={showSecondaryReadonlyConnectionStr ? secondaryReadonlyConnectionStr : maskedValue}
|
|
readOnly
|
|
styles={textfieldStyles}
|
|
{...(showSecondaryReadonlyConnectionStr && {
|
|
onRenderSuffix: () => renderCopyButton("#secondaryReadonlyConStrTextfield"),
|
|
})}
|
|
/>
|
|
<IconButton
|
|
iconProps={{ iconName: showSecondaryReadonlyConnectionStr ? "Hide3" : "View" }}
|
|
onClick={() => setShowSecondaryReadonlyConnectionStr(!showSecondaryReadonlyConnectionStr)}
|
|
styles={themeAwareIconButtonStyles}
|
|
/>
|
|
</Stack>
|
|
</Stack>
|
|
</PivotItem>
|
|
</Pivot>
|
|
|
|
<Stack style={{ margin: 10 }}>
|
|
<Text
|
|
style={{
|
|
fontWeight: 600,
|
|
marginBottom: 8,
|
|
color: "var(--colorNeutralForeground1)",
|
|
}}
|
|
>
|
|
Download sample app
|
|
</Text>
|
|
<Text
|
|
style={{
|
|
marginBottom: 8,
|
|
color: "var(--colorNeutralForeground2)",
|
|
}}
|
|
>
|
|
Don't have an app ready? No worries, download one of our sample app with a platform of your choice.
|
|
Connection string is already included in the app.
|
|
</Text>
|
|
<PrimaryButton
|
|
style={{ width: 185 }}
|
|
onClick={() =>
|
|
sendMessage({
|
|
type: MessageTypes.OpenQuickstartBlade,
|
|
})
|
|
}
|
|
text="Download sample app"
|
|
/>
|
|
</Stack>
|
|
</div>
|
|
);
|
|
};
|