diff --git a/src/ConnectExplorer.tsx b/src/ConnectExplorer.tsx new file mode 100644 index 000000000..f088da59f --- /dev/null +++ b/src/ConnectExplorer.tsx @@ -0,0 +1,78 @@ +import * as React from "react"; +import { useBoolean } from "@uifabric/react-hooks"; +import { HttpHeaders } from "./Common/Constants"; +import { GenerateTokenResponse } from "./Contracts/DataModels"; +import { configContext } from "./ConfigContext"; + +interface Props { + login: () => void; + setEncryptedToken: (token: string) => void; +} + +export const ConnectExplorer: React.FunctionComponent = ({ setEncryptedToken, login }: Props) => { + const [connectionString, setConnectionString] = React.useState(""); + const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false); + + return ( +
+
+
+

+ Azure Cosmos DB +

+

Welcome to Azure Cosmos DB

+ {isConnectionStringVisible ? ( +
{ + event.preventDefault(); + const headers = new Headers(); + headers.append(HttpHeaders.connectionString, connectionString); + const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken"; + const response = await fetch(url, { headers, method: "POST" }); + if (!response.ok) { + throw response; + } + // This API has a quirk where it must be parsed twice + const result: GenerateTokenResponse = JSON.parse(await response.json()); + console.log(result.readWrite || result.read); + setEncryptedToken(decodeURIComponent(result.readWrite || result.read)); + }} + > +

Connect to your account with connection string

+

+ { + setConnectionString(event.target.value); + }} + /> + + Error notification + + +

+

+ +

+

+ Sign In with Azure Account +

+
+ ) : ( +
+ +

+ Connect to your account with connection string +

+
+ )} +
+
+
+ ); +}; diff --git a/src/DirectoryPickerPanel.tsx b/src/DirectoryPickerPanel.tsx new file mode 100644 index 000000000..f8b3e988b --- /dev/null +++ b/src/DirectoryPickerPanel.tsx @@ -0,0 +1,50 @@ +import { Panel, PanelType, ChoiceGroup } from "office-ui-fabric-react"; +import * as React from "react"; +import { useDirectories } from "./hooks/useDirectories"; + +interface Props { + isOpen: boolean; + dismissPanel: () => void; + tenantId: string; + armToken: string; +} + +export const DirectoryPickerPanel: React.FunctionComponent = ({ + isOpen, + dismissPanel, + armToken, + tenantId +}: Props) => { + const directories = useDirectories(armToken); + return ( + + ({ key: dir.tenantId, text: `${dir.displayName} (${dir.tenantId})` }))} + selectedKey={tenantId} + onChange={async () => { + dismissPanel(); + // TODO!!! This does not work. Still not sure why. Tried lots of stuff. + // const response = await msal.loginPopup({ + // authority: `https://login.microsoftonline.com/${option.key}` + // }); + // // msal = new Msal.UserAgentApplication({ + // // auth: { + // // authority: `https://login.microsoftonline.com/${option.key}`, + // // clientId: "203f1145-856a-4232-83d4-a43568fba23d", + // // redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development + // // } + // // }); + // setTenantId(option.key); + // setAccount(response.account); + // console.log(account); + }} + /> + + ); +}; diff --git a/src/FeedbackCommandButton.tsx b/src/FeedbackCommandButton.tsx new file mode 100644 index 000000000..39bd535a2 --- /dev/null +++ b/src/FeedbackCommandButton.tsx @@ -0,0 +1,22 @@ +import * as React from "react"; +import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent"; +import FeedbackIcon from "../images/Feedback.svg"; + +export const FeedbackCommandButton: React.FunctionComponent = () => { + return ( +
+ + window.open("https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback") + } + ariaLabel="feeback button" + tooltipText="Send feedback" + hasPopup={true} + disabled={false} + /> +
+ ); +}; diff --git a/src/HostedExplorer.tsx b/src/HostedExplorer.tsx index 69b335f1d..f750613e9 100644 --- a/src/HostedExplorer.tsx +++ b/src/HostedExplorer.tsx @@ -1,34 +1,22 @@ -import "./Platform/Hosted/ConnectScreen.less"; import { useBoolean } from "@uifabric/react-hooks"; -import { - DefaultButton, - DirectionalHint, - FocusZone, - initializeIcons, - Panel, - PanelType, - Persona, - PersonaInitialsColor, - PersonaSize, - ChoiceGroup -} from "office-ui-fabric-react"; +import * as Msal from "msal"; +import { initializeIcons } from "office-ui-fabric-react"; import * as React from "react"; import { render } from "react-dom"; -import FeedbackIcon from "../images/Feedback.svg"; import ChevronRight from "../images/chevron-right.svg"; import "../less/hostedexplorer.less"; -import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent"; -import "./Explorer/Menus/NavBar/MeControlComponent.less"; -import { useGraphPhoto } from "./hooks/useGraphPhoto"; -import "./Shared/appInsights"; -import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent"; -import { usePortalAccessToken } from "./hooks/usePortalAccessToken"; -import { useDirectories } from "./hooks/useDirectories"; -import * as Msal from "msal"; -import { configContext } from "./ConfigContext"; -import { HttpHeaders } from "./Common/Constants"; -import { GenerateTokenResponse, DatabaseAccount } from "./Contracts/DataModels"; import { AuthType } from "./AuthType"; +import { ConnectExplorer } from "./ConnectExplorer"; +import { DatabaseAccount } from "./Contracts/DataModels"; +import { DirectoryPickerPanel } from "./DirectoryPickerPanel"; +import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent"; +import "./Explorer/Menus/NavBar/MeControlComponent.less"; +import { FeedbackCommandButton } from "./FeedbackCommandButton"; +import { usePortalAccessToken } from "./hooks/usePortalAccessToken"; +import { MeControl } from "./MeControl"; +import "./Platform/Hosted/ConnectScreen.less"; +import "./Shared/appInsights"; +import { SignInButton } from "./SignInButton"; initializeIcons(); @@ -58,9 +46,8 @@ const App: React.FunctionComponent = () => { const [encryptedToken, setEncryptedToken] = React.useState(params && params.get("key")); const encryptedTokenMetadata = usePortalAccessToken(encryptedToken); - // Hooks for showing/hiding UI + // Hooks for showing/hiding panel const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false); - const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false); // Hooks for AAD authentication const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean( @@ -70,11 +57,8 @@ const App: React.FunctionComponent = () => { const [tenantId, setTenantId] = React.useState(cachedTenantId); const [graphToken, setGraphToken] = React.useState(); const [armToken, setArmToken] = React.useState(); - const [connectionString, setConnectionString] = React.useState(""); const [databaseAccount, setDatabaseAccount] = React.useState(); - const ref = React.useRef(); - const login = React.useCallback(async () => { const response = await msal.loginPopup(); setLoggedIn(); @@ -89,6 +73,8 @@ const App: React.FunctionComponent = () => { msal.logout(); }, []); + const ref = React.useRef(); + React.useEffect(() => { if (account && tenantId) { Promise.all([ @@ -120,11 +106,8 @@ const App: React.FunctionComponent = () => { } }, [ref, encryptedToken, encryptedTokenMetadata, isLoggedIn, databaseAccount]); - const photo = useGraphPhoto(graphToken); - const directories = useDirectories(armToken); - return ( -
+ <>
@@ -151,77 +134,12 @@ const App: React.FunctionComponent = () => { )}
-
- - window.open("https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback") - } - ariaLabel="feeback button" - tooltipText="Send feedback" - hasPopup={true} - disabled={false} - /> -
+
{isLoggedIn ? ( - - - - - + ) : ( - + )}
@@ -242,102 +160,9 @@ const App: React.FunctionComponent = () => { src="explorer.html?v=1.0.1&platform=Hosted" > )} - {!isLoggedIn && !encryptedTokenMetadata && ( -
-
-
-

- Azure Cosmos DB -

-

Welcome to Azure Cosmos DB

- {isConnectionStringVisible ? ( -
{ - event.preventDefault(); - const headers = new Headers(); - headers.append(HttpHeaders.connectionString, connectionString); - const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken"; - const response = await fetch(url, { headers, method: "POST" }); - if (!response.ok) { - throw response; - } - // This API has a quirk where it must be parsed twice - const result: GenerateTokenResponse = JSON.parse(await response.json()); - console.log(result.readWrite || result.read); - setEncryptedToken(decodeURIComponent(result.readWrite || result.read)); - }} - > -

- Connect to your account with connection string -

-

- { - setConnectionString(event.target.value); - }} - /> - - Error notification - - -

-

- -

-

- Sign In with Azure Account -

-
- ) : ( -
- -

- Connect to your account with connection string -

-
- )} -
-
-
- )} -
-
- - ({ key: dir.tenantId, text: `${dir.displayName} (${dir.tenantId})` }))} - selectedKey={tenantId} - onChange={async () => { - dismissPanel(); - // TODO!!! This does not work. Still not sure why. Tried lots of stuff. - // const response = await msal.loginPopup({ - // authority: `https://login.microsoftonline.com/${option.key}` - // }); - // // msal = new Msal.UserAgentApplication({ - // // auth: { - // // authority: `https://login.microsoftonline.com/${option.key}`, - // // clientId: "203f1145-856a-4232-83d4-a43568fba23d", - // // redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development - // // } - // // }); - // setTenantId(option.key); - // setAccount(response.account); - // console.log(account); - }} - /> - -
+ {!isLoggedIn && !encryptedTokenMetadata && } + + ); }; diff --git a/src/MeControl.tsx b/src/MeControl.tsx new file mode 100644 index 000000000..e485badcf --- /dev/null +++ b/src/MeControl.tsx @@ -0,0 +1,68 @@ +import { + FocusZone, + DefaultButton, + DirectionalHint, + Persona, + PersonaInitialsColor, + PersonaSize +} from "office-ui-fabric-react"; +import * as React from "react"; +import { Account } from "msal"; +import { useGraphPhoto } from "./hooks/useGraphPhoto"; + +interface Props { + graphToken: string; + account: Account; + openPanel: () => void; + logout: () => void; +} + +export const MeControl: React.FunctionComponent = ({ openPanel, logout, account, graphToken }: Props) => { + const photo = useGraphPhoto(graphToken); + return ( + + + + + + ); +}; diff --git a/src/SignInButton.tsx b/src/SignInButton.tsx new file mode 100644 index 000000000..140332845 --- /dev/null +++ b/src/SignInButton.tsx @@ -0,0 +1,18 @@ +import { DefaultButton } from "office-ui-fabric-react"; +import * as React from "react"; + +interface Props { + login: () => void; + } + +export const SignInButton: React.FunctionComponent = ({ login }: Props) => { + return ; +};