updated tabs , sidebar , splas screen

This commit is contained in:
Sakshi Gupta
2025-04-23 18:52:28 +05:30
parent b5976fb034
commit 8dd2571444
10 changed files with 865 additions and 711 deletions

View File

@@ -130,7 +130,6 @@ const useSidebarStyles = makeStyles({
treeContainer: {
flex: 1,
overflow: "auto",
paddingLeft: tokens.spacingHorizontalM,
backgroundColor: tokens.colorNeutralBackground1,
color: tokens.colorNeutralForeground1,
},

View File

@@ -1,178 +1,178 @@
@import "../../../less/Common/Constants";
// @import "../../../less/Common/Constants";
.splashScreenContainer {
width: 100%;
overflow-y: auto;
overflow-x: hidden;
// .splashScreenContainer {
// width: 100%;
// overflow-y: scroll;
// overflow-x: hidden;
.splashScreen {
.flex-display();
.flex-direction();
text-align: left;
margin: auto;
padding-left: 21px;
padding-right: 16px;
max-width: 1168px;
// .splashScreen {
// .flex-display();
// .flex-direction();
// text-align: left;
// margin: auto;
// padding-left: 21px;
// padding-right: 16px;
// max-width: 1168px;
> .title {
position: relative; // To attach FeaturePanelLauncher as absolute
color: @BaseHigh;
font-size: 48px;
padding-left: 0px;
margin: 16px auto;
text-align: center;
}
// > .title {
// position: relative; // To attach FeaturePanelLauncher as absolute
// color: @BaseHigh;
// font-size: 48px;
// padding-left: 0px;
// margin: 16px auto;
// text-align: center;
// }
> .subtitle {
color: @BaseHigh;
font-size: 18px;
padding-left: 0px;
margin: 0px auto;
text-align: center;
}
// > .subtitle {
// color: @BaseHigh;
// font-size: 18px;
// padding-left: 0px;
// margin: 0px auto;
// text-align: center;
// }
.mainButtonsContainer {
.flex-display();
text-align: center;
cursor: pointer;
margin: 40px auto;
width: 84%;
// .mainButtonsContainer {
// .flex-display();
// text-align: center;
// cursor: pointer;
// margin: 40px auto;
// width: 84%;
> .mainButton {
min-width: 124px;
max-width: 296px;
padding: 32px 16px;
background-color: @BaseLight;
border: 1px solid #949494;
box-sizing: border-box;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
border-radius: 4px;
// > .mainButton {
// min-width: 124px;
// max-width: 296px;
// padding: 32px 16px;
// background-color: @BaseLight;
// border: 1px solid #949494;
// box-sizing: border-box;
// box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
// border-radius: 4px;
> .legendContainer {
margin-left: 16px;
text-align: left;
// > .legendContainer {
// margin-left: 16px;
// text-align: left;
.legend {
font-family: @SemiboldFont;
font-size: 18px;
}
// .legend {
// font-family: @SemiboldFont;
// font-size: 18px;
// }
.description {
font-size: 10px;
}
// .description {
// font-size: 10px;
// }
.newDescription {
font-size: 13px;
}
}
}
// .newDescription {
// font-size: 13px;
// }
// }
// }
> :nth-child(n + 2) {
margin-left: 32px;
}
}
// > :nth-child(n + 2) {
// margin-left: 32px;
// }
// }
.moreStuffContainer {
.flex-display();
justify-content: space-between;
// .moreStuffContainer {
// .flex-display();
// justify-content: space-between;
.moreStuffColumn {
flex-grow: 1;
flex-basis: 0;
min-width: 124px;
max-width: 296px;
// .moreStuffColumn {
// flex-grow: 1;
// flex-basis: 0;
// min-width: 124px;
// max-width: 296px;
> .title {
font-size: 18px;
font-family: @SemiboldFont;
color: @BaseDark;
padding: 0px;
margin-bottom: 16px;
}
// > .title {
// font-size: 18px;
// font-family: @SemiboldFont;
// color: @BaseDark;
// padding: 0px;
// margin-bottom: 16px;
// }
> ul {
list-style: none;
padding-left: 0px;
margin-bottom: 0px;
// > ul {
// list-style: none;
// padding-left: 0px;
// margin-bottom: 0px;
li {
padding: @DefaultSpace;
.flex-display();
align-items: flex-start;
// li {
// padding: @DefaultSpace;
// .flex-display();
// align-items: flex-start;
> img {
margin-right: @DefaultSpace;
width: 24px;
height: 24px;
}
// > img {
// margin-right: @DefaultSpace;
// width: 24px;
// height: 24px;
// }
.oneLineContent {
margin-top: 4px;
}
// .oneLineContent {
// margin-top: 4px;
// }
.description {
font-size: 10px;
color: @BaseMediumHigh;
}
}
}
// .description {
// font-size: 10px;
// color: @BaseMediumHigh;
// }
// }
// }
.tipContainer {
padding: 8px 16px;
width: 100%;
cursor: pointer;
.flex-display();
.flex-direction();
// .tipContainer {
// padding: 8px 16px;
// width: 100%;
// cursor: pointer;
// .flex-display();
// .flex-direction();
> .title {
color: @BaseDark;
padding: 0px;
font-size: 12px;
}
> .description {
color: @BaseDark;
}
// > .title {
// color: @BaseDark;
// padding: 0px;
// font-size: 12px;
// }
// > .description {
// color: @BaseDark;
// }
&:not(:hover):not(:focus) {
background-color: @BaseLow;
}
}
// &:not(:hover):not(:focus) {
// background-color: @BaseLow;
// }
// }
&.commonTasks {
li {
cursor: pointer;
}
}
// &.commonTasks {
// li {
// cursor: pointer;
// }
// }
&.tipsContainer {
li {
margin: 2px 0px;
}
}
}
}
// &.tipsContainer {
// li {
// margin: 2px 0px;
// }
// }
// }
// }
.focusable {
&:hover {
.hover();
}
// .focusable {
// &:hover {
// .hover();
// }
&:focus {
.focus();
}
// &:focus {
// .focus();
// }
&:active {
.active();
}
}
// &:active {
// .active();
// }
// }
.notebookSplashScreenItem {
padding: 12px 0 12px 12px;
// .notebookSplashScreenItem {
// padding: 12px 0 12px 12px;
.itemText {
margin-left: 12px;
font-family: @SemiboldFont;
}
}
}
}
// .itemText {
// margin-left: 12px;
// font-family: @SemiboldFont;
// }
// }
// }
// }

View File

@@ -11,6 +11,7 @@ import {
TeachingBubbleContent,
Text,
} from "@fluentui/react";
import { makeStyles, shorthands } from "@fluentui/react-components";
import { sendMessage } from "Common/MessageHandler";
import { MessageTypes } from "Contracts/ExplorerContracts";
import { TerminalKind } from "Contracts/ViewModels";
@@ -33,8 +34,7 @@ import CollectionIcon from "../../../images/tree-collection.svg";
import * as Constants from "../../Common/Constants";
import { userContext } from "../../UserContext";
import { getCollectionName } from "../../Utils/APITypeUtils";
import { FeaturePanelLauncher } from "../Controls/FeaturePanel/FeaturePanelLauncher";
import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil";
import { useTheme } from "../../hooks/useTheme";
import Explorer from "../Explorer";
import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity";
import { useNotebook } from "../Notebook/useNotebook";
@@ -55,70 +55,177 @@ export interface SplashScreenProps {
explorer: Explorer;
}
export class SplashScreen extends React.Component<SplashScreenProps> {
private readonly container: Explorer;
private subscriptions: Array<{ dispose: () => void }>;
const useStyles = makeStyles({
splashScreenContainer: {
display: "flex",
flexDirection: "column",
alignItems: "center",
// justifyContent: "center",
minHeight: "100vh",
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)",
constructor(props: SplashScreenProps) {
super(props);
this.container = props.explorer;
this.subscriptions = [];
}
public componentWillUnmount(): void {
while (this.subscriptions.length) {
this.subscriptions.pop().dispose();
},
splashScreen: {
display: "flex",
// overflow: "scroll",
flexDirection: "column",
alignItems: "center",
textAlign: "left",
// ...shorthands.padding("40px")
},
title: {
fontSize: "48px",
fontWeight: "500",
margin: "16px auto",
color: "var(--colorNeutralForeground1)"
},
subtitle: {
fontSize: "18px",
marginBottom: "40px",
color: "var(--colorNeutralForeground2)"
},
cardContainer: {
display: "grid",
gridTemplateColumns: "repeat(2, 1fr)",
gap: "16px",
width: "66%",
margin: "0 auto",
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)",
},
card: {
display: "flex",
flexDirection: "column",
alignItems: "left",
...shorthands.padding("32px", "16px"),
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)",
border: "1px solid var(--colorNeutralStroke1)",
borderRadius: "4px",
boxShadow: "var(--shadow4)",
cursor: "pointer",
minHeight: "150px",
"&:hover": {
backgroundColor: "var(--colorNeutralBackground1Hover)"
}
},
cardContent: {
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
marginLeft: "16px",
textAlign: "left",
color: "var(--colorNeutralForeground1)"
},
cardTitle: {
fontSize: "18px",
fontWeight: "600",
color: "var(--colorNeutralForeground1)",
textAlign: "left",
marginBottom: "8px"
},
cardDescription: {
fontSize: "13px",
color: "var(--colorNeutralForeground2)",
textAlign: "left"
},
moreStuffContainer: {
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gap: "32px",
width: "66%",
margin: "40px auto",
},
moreStuffColumn: {
display: "flex",
flexDirection: "column",
// justifyContent:"space-between"
},
columnTitle: {
fontSize: "20px",
fontWeight: "600",
marginBottom: "16px",
color: "var(--colorNeutralForeground1)"
},
listItem: {
marginBottom: "26px",
},
listItemTitle: {
fontSize: "14px",
color: "var(--colorBrandForegroundLink)",
"&:hover": {
color: "var(--colorBrandForegroundLink)"
}
},
listItemSubtitle: {
fontSize: "13px",
color: "var(--colorNeutralForeground2)"
}
});
public componentDidMount(): void {
this.subscriptions.push(
export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
const styles = useStyles();
const { isDarkMode } = useTheme();
const container = explorer;
const subscriptions: Array<{ dispose: () => void }> = [];
React.useEffect(() => {
subscriptions.push(
{
dispose: useNotebook.subscribe(
() => this.setState({}),
() => setState({}),
(state) => state.isNotebookEnabled,
),
},
{ dispose: useSelectedNode.subscribe(() => this.setState({})) },
{ dispose: useSelectedNode.subscribe(() => setState({})) },
{
dispose: useCarousel.subscribe(
() => this.setState({}),
() => setState({}),
(state) => state.showCoachMark,
),
},
{
dispose: usePostgres.subscribe(
() => this.setState({}),
() => setState({}),
(state) => state.showPostgreTeachingBubble,
),
},
{
dispose: usePostgres.subscribe(
() => this.setState({}),
() => setState({}),
(state) => state.showResetPasswordBubble,
),
},
{
dispose: useDatabases.subscribe(
() => this.setState({}),
() => setState({}),
(state) => state.sampleDataResourceTokenCollection,
),
},
{
dispose: useQueryCopilot.subscribe(
() => this.setState({}),
() => setState({}),
(state) => state.copilotEnabled,
),
},
);
}
private clearMostRecent = (): void => {
return () => {
while (subscriptions.length) {
subscriptions.pop().dispose();
}
};
}, []);
const [state, setState] = React.useState({});
const clearMostRecent = () => {
MostRecentActivity.clear(userContext.databaseAccount?.name);
this.setState({});
setState({});
};
private getSplashScreenButtons = (): JSX.Element => {
const getSplashScreenButtons = (): JSX.Element => {
if (
userContext.apiType === "SQL" &&
useQueryCopilot.getState().copilotEnabled &&
@@ -132,7 +239,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
title={"Launch quick start"}
description={"Launch a quick start tutorial to get started with sample data"}
onClick={() => {
this.container.onNewCollectionClicked({ isQuickstart: true });
container.onNewCollectionClicked({ isQuickstart: true });
traceOpen(Action.LaunchQuickstart, { apiType: userContext.apiType });
}}
/>
@@ -141,7 +248,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
title={`New ${getCollectionName()}`}
description={"Create a new container for storage and throughput"}
onClick={() => {
this.container.onNewCollectionClicked();
container.onNewCollectionClicked();
traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType });
}}
/>
@@ -177,7 +284,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
);
}
const mainItems = this.createMainItems();
const mainItems = createMainItems();
return (
<div className="mainButtonsContainer">
{userContext.apiType === "Postgres" &&
@@ -214,7 +321,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
className="mainButton focusable"
key={`${item.title}`}
onClick={item.onClick}
onKeyPress={(event: React.KeyboardEvent) => this.onSplashScreenItemKeyPress(event, item.onClick)}
onKeyPress={(event: React.KeyboardEvent) => onSplashScreenItemKeyPress(event, item.onClick)}
tabIndex={0}
role="button"
>
@@ -267,125 +374,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
);
};
public render(): JSX.Element {
let title: string;
let subtitle: string;
switch (userContext.apiType) {
case "Postgres":
title = "Welcome to Azure Cosmos DB for PostgreSQL";
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
break;
case "VCoreMongo":
title = "Welcome to Azure Cosmos DB for MongoDB (vCore)";
subtitle = "Get started with our sample datasets, documentation, and additional tools.";
break;
default:
title = "Welcome to Azure Cosmos DB";
subtitle = "Globally distributed, multi-model database service for any scale";
}
return (
<div className="connectExplorerContainer">
<form className="connectExplorerFormContainer">
<div className="splashScreenContainer">
<div className="splashScreen">
<h1 className="title" role="heading" aria-label={title}>
{title}
<FeaturePanelLauncher />
</h1>
<div className="subtitle">{subtitle}</div>
{this.getSplashScreenButtons()}
{useCarousel.getState().showCoachMark && (
<Coachmark
target="#quickstartDescription"
positioningContainerProps={{ directionalHint: DirectionalHint.rightTopEdge }}
persistentBeak
>
<TeachingBubbleContent
headline={`Start with sample ${getCollectionName().toLocaleLowerCase()}`}
hasCloseButton
closeButtonAriaLabel="Close"
primaryButtonProps={{
text: "Get started",
onClick: () => {
useCarousel.getState().setShowCoachMark(false);
this.container.onNewCollectionClicked({ isQuickstart: true });
},
}}
secondaryButtonProps={{
text: "Cancel",
onClick: () => useCarousel.getState().setShowCoachMark(false),
}}
onDismiss={() => useCarousel.getState().setShowCoachMark(false)}
>
You will be guided to create a sample container with sample data, then we will give you a tour of
data explorer. You can also cancel launching this tour and explore yourself
</TeachingBubbleContent>
</Coachmark>
)}
{userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo" ? (
<Stack horizontal style={{ margin: "0 auto", width: "84%" }} tokens={{ childrenGap: 16 }}>
<Stack style={{ width: "33%" }}>
<Text
variant="large"
style={{
marginBottom: 16,
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Next steps
</Text>
{this.getNextStepItems()}
</Stack>
<Stack style={{ width: "33%" }}>
<Text
variant="large"
style={{
marginBottom: 16,
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Tips & learn more
</Text>
{this.getTipsAndLearnMoreItems()}
</Stack>
<Stack style={{ width: "33%" }}></Stack>
</Stack>
) : (
<div className="moreStuffContainer">
<div className="moreStuffColumn commonTasks">
<h2 className="title">Recents</h2>
{this.getRecentItems()}
</div>
<div className="moreStuffColumn">
<h2 className="title">Top 3 things you need to know</h2>
{this.top3Items()}
</div>
<div className="moreStuffColumn tipsContainer">
<h2 className="title">Learning Resources</h2>
{this.getLearningResourceItems()}
</div>
</div>
)}
</div>
</div>
</form>
</div>
);
}
/**
* This exists to enable unit testing
*/
public createDataSampleUtil(): DataSamplesUtil {
return new DataSamplesUtil(this.container);
}
/**
* public for testing purposes
*/
public createMainItems(): SplashScreenItem[] {
const createMainItems = (): SplashScreenItem[] => {
const heroes: SplashScreenItem[] = [];
if (
@@ -403,7 +392,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
if (userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo") {
useTabs.getState().openAndActivateReactTab(ReactTabKind.Quickstart);
} else {
this.container.onNewCollectionClicked({ isQuickstart: true });
container.onNewCollectionClicked({ isQuickstart: true });
}
traceOpen(Action.LaunchQuickstart, { apiType: userContext.apiType });
},
@@ -411,18 +400,18 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
heroes.push(launchQuickstartBtn);
}
heroes.push(this.getShellCard());
heroes.push(this.getThirdCard());
heroes.push(getShellCard());
heroes.push(getThirdCard());
return heroes;
}
};
private getShellCard() {
const getShellCard = (): SplashScreenItem => {
if (userContext.apiType === "Postgres") {
return {
iconSrc: PowerShellIcon,
title: "PostgreSQL Shell",
description: "Create table and interact with data using PostgreSQLs shell interface",
onClick: () => this.container.openNotebookTerminal(TerminalKind.Postgres),
description: "Create table and interact with data using PostgreSQL's shell interface",
onClick: () => container.openNotebookTerminal(TerminalKind.Postgres),
};
}
@@ -431,7 +420,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
iconSrc: PowerShellIcon,
title: "Mongo Shell",
description: "Create a collection and interact with data using MongoDB's shell interface",
onClick: () => this.container.openNotebookTerminal(TerminalKind.VCoreMongo),
onClick: () => container.openNotebookTerminal(TerminalKind.VCoreMongo),
};
}
@@ -440,13 +429,13 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
title: `New ${getCollectionName()}`,
description: "Create a new container for storage and throughput",
onClick: () => {
this.container.onNewCollectionClicked();
container.onNewCollectionClicked();
traceOpen(Action.NewContainerHomepage, { apiType: userContext.apiType });
},
};
}
};
private getThirdCard() {
const getThirdCard = (): SplashScreenItem => {
let icon = ConnectIcon;
let title = "Connect";
let description = "Prefer using your own choice of tooling? Find the connection string you need to connect";
@@ -470,34 +459,34 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
description: description,
onClick: onClick,
};
}
};
private decorateOpenCollectionActivity({ databaseId, collectionId }: MostRecentActivity.OpenCollectionItem) {
const decorateOpenCollectionActivity = (activity: MostRecentActivity.OpenCollectionItem): SplashScreenItem => {
return {
iconSrc: CollectionIcon,
title: collectionId,
title: activity.collectionId,
description: getCollectionName(),
onClick: () => {
const collection = useDatabases.getState().findCollection(databaseId, collectionId);
const collection = useDatabases.getState().findCollection(activity.databaseId, activity.collectionId);
collection?.openTab();
},
};
}
};
private decorateOpenNotebookActivity({ name, path }: MostRecentActivity.OpenNotebookItem) {
const decorateOpenNotebookActivity = (activity: MostRecentActivity.OpenNotebookItem): SplashScreenItem => {
return {
info: path,
info: activity.path,
iconSrc: NotebookIcon,
title: name,
title: activity.name,
description: "Notebook",
onClick: () => {
const notebookItem = this.container.createNotebookContentItemFile(name, path);
notebookItem && this.container.openNotebook(notebookItem);
const notebookItem = container.createNotebookContentItemFile(activity.name, activity.path);
notebookItem && container.openNotebook(notebookItem);
},
};
}
};
private createRecentItems(): SplashScreenItem[] {
const createRecentItems = (): SplashScreenItem[] => {
return MostRecentActivity.getItems(userContext.databaseAccount?.name).map((activity) => {
switch (activity.type) {
default: {
@@ -505,22 +494,22 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
throw new Error(`Unknown activity: ${unknownActivity}`);
}
case MostRecentActivity.Type.OpenNotebook:
return this.decorateOpenNotebookActivity(activity);
return decorateOpenNotebookActivity(activity);
case MostRecentActivity.Type.OpenCollection:
return this.decorateOpenCollectionActivity(activity);
return decorateOpenCollectionActivity(activity);
}
});
}
};
private onSplashScreenItemKeyPress(event: React.KeyboardEvent, callback: () => void) {
const onSplashScreenItemKeyPress = (event: React.KeyboardEvent, callback: () => void) => {
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
callback();
event.stopPropagation();
}
}
};
private top3Items(): JSX.Element {
const top3Items = (): JSX.Element => {
let items: { link: string; title: string; description: string }[];
switch (userContext.apiType) {
case "SQL":
@@ -632,44 +621,54 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
href={item.link}
target="_blank"
style={{ marginRight: 5 }}
className={styles.listItemTitle}
>
{item.title}
</Link>
<Image src={LinkIcon} alt={item.title} />
</Stack>
<Text>{item.description}</Text>
<Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack>
))}
</Stack>
);
}
};
private getRecentItems(): JSX.Element {
const recentItems = this.createRecentItems()?.filter((item) => item.description !== "Notebook");
const getRecentItems = (): JSX.Element => {
const recentItems = createRecentItems()?.filter((item) => item.description !== "Notebook");
return (
<Stack>
<ul>
{recentItems.map((item, index) => (
<li key={`${item.title}${item.description}${index}`}>
<li key={`${item.title}${item.description}${index}`} className={styles.listItem}>
<Stack style={{ marginBottom: 26 }}>
<Stack horizontal>
<Image style={{ marginRight: 8 }} src={item.iconSrc} alt={item.title} />
<Link style={{ fontSize: 14 }} onClick={item.onClick} title={item.info}>
<svg
width="16"
height="16"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
style={{ marginRight: 8 }}
fill="currentColor"
>
<path d="M4 4c0-1.1.9-2 2-2h3.59c.4 0 .78.16 1.06.44l3.91 3.91c.28.28.44.67.44 1.06V14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4Zm2-1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V8h-3.5A1.5 1.5 0 0 1 9 6.5V3H6Zm4 .2v3.3c0 .28.22.5.5.5h3.3L10 3.2ZM17 9a1 1 0 0 0-1-1v6a3 3 0 0 1-3 3H6a1 1 0 0 0 1 1h6.06A3.94 3.94 0 0 0 17 14.06V9Z" />
</svg>
<Link style={{ fontSize: 14 }} onClick={item.onClick} title={item.info} className={styles.listItemTitle}>
{item.title}
</Link>
</Stack>
<Text style={{ color: "#605E5C" }}>{item.description}</Text>
<Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack>
</li>
))}
</ul>
{recentItems.length > 0 && <Link onClick={() => this.clearMostRecent()}>Clear Recents</Link>}
{recentItems.length > 0 && <Link onClick={() => clearMostRecent()} className={styles.listItemTitle}>Clear Recents</Link>}
</Stack>
);
}
};
private getLearningResourceItems(): JSX.Element {
const getLearningResourceItems = (): JSX.Element => {
interface item {
link: string;
title: string;
@@ -785,19 +784,20 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
href={item.link}
target="_blank"
style={{ marginRight: 5 }}
className={styles.listItemTitle}
>
{item.title}
</Link>
<Image src={LinkIcon} alt={item.title} />
</Stack>
<Text>{item.description}</Text>
<Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack>
))}
</Stack>
);
}
};
private postgresNextStepItems: { link: string; title: string; description: string }[] = [
const postgresNextStepItems: { link: string; title: string; description: string }[] = [
{
link: "https://go.microsoft.com/fwlink/?linkid=2208312",
title: "Data Modeling",
@@ -815,7 +815,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
},
];
private vcoreMongoNextStepItems: { link: string; title: string; description: string }[] = [
const vcoreMongoNextStepItems: { link: string; title: string; description: string }[] = [
{
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/how-to-migrate-native-tools?tabs=export-import",
title: "Migrate Data",
@@ -833,27 +833,27 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
},
];
private getNextStepItems(): JSX.Element {
const items = userContext.apiType === "Postgres" ? this.postgresNextStepItems : this.vcoreMongoNextStepItems;
const getNextStepItems = (): JSX.Element => {
const items = userContext.apiType === "Postgres" ? postgresNextStepItems : vcoreMongoNextStepItems;
return (
<Stack style={{ minWidth: 124, maxWidth: 296 }}>
{items.map((item, i) => (
<Stack key={`nextStep${i}`} style={{ marginBottom: 26 }}>
<Stack horizontal verticalAlign="center" style={{ fontSize: 14 }}>
<Link href={item.link} target="_blank" style={{ marginRight: 5 }}>
<Link href={item.link} target="_blank" style={{ marginRight: 5 }} className={styles.listItemTitle}>
{item.title}
</Link>
<Image src={LinkIcon} />
</Stack>
<Text>{item.description}</Text>
<Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack>
))}
</Stack>
);
}
};
private postgresLearnMoreItems: { link: string; title: string; description: string }[] = [
const postgresLearnMoreItems: { link: string; title: string; description: string }[] = [
{
link: "https://go.microsoft.com/fwlink/?linkid=2207226",
title: "Performance Tuning",
@@ -871,7 +871,7 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
},
];
private vcoreMongoLearnMoreItems: { link: string; title: string; description: string }[] = [
const vcoreMongoLearnMoreItems: { link: string; title: string; description: string }[] = [
{
link: "https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/vcore/vector-search",
title: "Vector Search",
@@ -889,23 +889,109 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
},
];
private getTipsAndLearnMoreItems(): JSX.Element {
const items = userContext.apiType === "Postgres" ? this.postgresLearnMoreItems : this.vcoreMongoLearnMoreItems;
const getTipsAndLearnMoreItems = (): JSX.Element => {
const items = userContext.apiType === "Postgres" ? postgresLearnMoreItems : vcoreMongoLearnMoreItems;
return (
<Stack style={{ minWidth: 124, maxWidth: 296 }}>
{items.map((item, i) => (
<Stack key={`tips${i}`} style={{ marginBottom: 26 }}>
<Stack horizontal verticalAlign="center" style={{ fontSize: 14 }}>
<Link href={item.link} target="_blank" style={{ marginRight: 5 }}>
<Link href={item.link} target="_blank" style={{ marginRight: 5 }} className={styles.listItemTitle}>
{item.title}
</Link>
<Image src={LinkIcon} />
</Stack>
<Text>{item.description}</Text>
<Text className={styles.listItemSubtitle}>{item.description}</Text>
</Stack>
))}
</Stack>
);
}
}
};
return (
<div className={styles.splashScreenContainer}>
<div className={styles.splashScreen}>
<h1 className={styles.title} role="heading" aria-label="Welcome to Azure Cosmos DB">
Welcome to Azure Cosmos DB<span className="activePatch"></span>
</h1>
<div className={styles.subtitle}>
Globally distributed, multi-model database service for any scale
</div>
{getSplashScreenButtons()}
{useCarousel.getState().showCoachMark && (
<Coachmark
target="#quickstartDescription"
positioningContainerProps={{ directionalHint: DirectionalHint.rightTopEdge }}
persistentBeak
>
<TeachingBubbleContent
headline={`Start with sample ${getCollectionName().toLocaleLowerCase()}`}
hasCloseButton
closeButtonAriaLabel="Close"
primaryButtonProps={{
text: "Get started",
onClick: () => {
useCarousel.getState().setShowCoachMark(false);
container.onNewCollectionClicked({ isQuickstart: true });
},
}}
secondaryButtonProps={{
text: "Cancel",
onClick: () => useCarousel.getState().setShowCoachMark(false),
}}
onDismiss={() => useCarousel.getState().setShowCoachMark(false)}
>
You will be guided to create a sample container with sample data, then we will give you a tour of
data explorer. You can also cancel launching this tour and explore yourself
</TeachingBubbleContent>
</Coachmark>
)}
{userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo" ? (
<Stack horizontal style={{ margin: "0 auto", width: "84%" }} tokens={{ childrenGap: 16 }}>
<Stack style={{ width: "33%" }}>
<Text
variant="large"
style={{
marginBottom: 16,
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Next steps
</Text>
{getNextStepItems()}
</Stack>
<Stack style={{ width: "33%" }}>
<Text
variant="large"
style={{
marginBottom: 16,
fontFamily: '"Segoe UI Semibold", "Segoe UI", "Segoe WP", Tahoma, Arial, sans-serif',
}}
>
Tips & learn more
</Text>
{getTipsAndLearnMoreItems()}
</Stack>
<Stack style={{ width: "33%" }}></Stack>
</Stack>
) : (
<div className={styles.moreStuffContainer}>
<div className={styles.moreStuffColumn}>
<h2 className={styles.columnTitle}>Recents</h2>
{getRecentItems()}
</div>
<div className={styles.moreStuffColumn}>
<h2 className={styles.columnTitle}>Top 3 things you need to know</h2>
{top3Items()}
</div>
<div className={styles.moreStuffColumn}>
<h2 className={styles.columnTitle}>Learning Resources</h2>
{getLearningResourceItems()}
</div>
</div>
)}
</div>
</div>
);
};

View File

@@ -1,4 +1,5 @@
import { Stack, Text } from "@fluentui/react";
import { makeStyles } from "@fluentui/react-components";
import React from "react";
import { KeyCodes } from "../../Common/Constants";
@@ -9,25 +10,50 @@ interface SplashScreenButtonProps {
onClick: () => void;
}
const useStyles = makeStyles({
button: {
border: "1px solid var(--colorNeutralStroke1)",
boxSizing: "border-box",
boxShadow: "var(--shadow4)",
borderRadius: "4px",
padding: "32px 16px",
backgroundColor: "var(--colorNeutralBackground1)",
color: "var(--colorNeutralForeground1)",
width: "100%",
minHeight: "150px",
cursor: "pointer",
"&:hover": {
backgroundColor: "var(--colorNeutralBackground1Hover)"
}
},
content: {
marginLeft: "16px",
textAlign: "left"
},
title: {
fontSize: "18px",
fontWeight: "600",
color: "var(--colorNeutralForeground1)",
marginBottom: "8px"
},
description: {
fontSize: "13px",
color: "var(--colorNeutralForeground2)"
}
});
export const SplashScreenButton: React.FC<SplashScreenButtonProps> = ({
imgSrc,
title,
description,
onClick,
}: SplashScreenButtonProps): JSX.Element => {
const styles = useStyles();
return (
<Stack
horizontal
style={{
border: "1px solid #949494",
boxSizing: "border-box",
boxShadow: "0 4px 4px rgba(0, 0, 0, 0.25)",
borderRadius: 4,
padding: "32px 16px",
backgroundColor: "#ffffff",
width: "100%",
minHeight: 150,
}}
className={styles.button}
onClick={onClick}
onKeyPress={(event: React.KeyboardEvent) => {
if (event.charCode === KeyCodes.Space || event.charCode === KeyCodes.Enter) {
@@ -41,9 +67,9 @@ export const SplashScreenButton: React.FC<SplashScreenButtonProps> = ({
<div>
<img src={imgSrc} alt={title} aria-hidden="true" />
</div>
<Stack style={{ marginLeft: 16 }}>
<Text style={{ fontSize: 18, fontWeight: 600 }}>{title}</Text>
<Text style={{ fontSize: 13 }}>{description}</Text>
<Stack className={styles.content}>
<Text className={styles.title}>{title}</Text>
<Text className={styles.description}>{description}</Text>
</Stack>
</Stack>
);

View File

@@ -1,3 +1,4 @@
import { Spinner, SpinnerSize } from "@fluentui/react";
import { CollectionTabKind } from "Contracts/ViewModels";
import Explorer from "Explorer/Explorer";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
@@ -15,8 +16,6 @@ import { userContext } from "UserContext";
import { useTeachingBubble } from "hooks/useTeachingBubble";
import ko from "knockout";
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
import loadingIcon from "../../../images/circular_loader_black_16x16.gif";
import errorIcon from "../../../images/close-black.svg";
import errorQuery from "../../../images/error_no_outline.svg";
import { useObservable } from "../../hooks/useObservable";
import { ReactTabKind, useTabs } from "../../hooks/useTabs";
@@ -126,7 +125,17 @@ function TabNav({ tab, active, tabKind }: { tab?: Tab; active: boolean; tabKind?
<span className="statusIconContainer" style={{ width: tabKind === ReactTabKind.Home ? 0 : 18 }}>
{useObservable(tab?.isExecutionError || ko.observable(false)) && <ErrorIcon tab={tab} active={active} />}
{isTabExecuting(tab, tabKind) && (
<img className="loadingIcon" title="Loading" src={loadingIcon} alt="Loading" />
<Spinner
size={SpinnerSize.small}
styles={{
circle: {
borderTopColor: "var(--colorNeutralForeground1)",
borderLeftColor: "var(--colorNeutralForeground1)",
borderBottomColor: "var(--colorNeutralForeground1)",
borderRightColor: "var(--colorNeutralBackground1)"
}
}}
/>
)}
{isQueryErrorThrown(tab, tabKind) && (
<img
@@ -177,14 +186,11 @@ const CloseButton = ({
onClick={(event: React.MouseEvent<HTMLSpanElement, MouseEvent>) => {
event.stopPropagation();
tab ? tab.onCloseTabButtonClick() : useTabs.getState().closeReactTab(tabKind);
// tabKind === ReactTabKind.QueryCopilot && useQueryCopilot.getState().resetQueryCopilotStates();
}}
tabIndex={active ? 0 : undefined}
onKeyPress={({ nativeEvent: e }) => (tab ? tab.onKeyPressClose(undefined, e) : onKeyPressReactTabClose(e, tabKind))}
>
<span className="tabIcon close-Icon">
<img src={errorIcon} title="Close" alt="Close" aria-label="hidden" />
</span>
<span className="tabIcon close-Icon" />
</span>
);
@@ -267,7 +273,6 @@ const isQueryErrorThrown = (tab?: Tab, tabKind?: ReactTabKind): boolean => {
};
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
// eslint-disable-next-line no-console
switch (activeReactTab) {
case ReactTabKind.Connect:
return userContext.apiType === "VCoreMongo" ? (
@@ -292,6 +297,6 @@ const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): J
case ReactTabKind.QueryCopilot:
return <QueryCopilotTab explorer={explorer} />;
default:
throw Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`);
throw new Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`);
}
};