mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-21 09:51:11 +00:00
WIP
This commit is contained in:
@@ -2,18 +2,24 @@ import "./Platform/Hosted/ConnectScreen.less";
|
|||||||
import { useBoolean } from "@uifabric/react-hooks";
|
import { useBoolean } from "@uifabric/react-hooks";
|
||||||
import {
|
import {
|
||||||
DefaultButton,
|
DefaultButton,
|
||||||
|
DetailsList,
|
||||||
DirectionalHint,
|
DirectionalHint,
|
||||||
FocusZone,
|
FocusZone,
|
||||||
|
IContextualMenuProps,
|
||||||
initializeIcons,
|
initializeIcons,
|
||||||
Panel,
|
Panel,
|
||||||
|
PanelType,
|
||||||
Persona,
|
Persona,
|
||||||
PersonaInitialsColor,
|
PersonaInitialsColor,
|
||||||
PersonaSize
|
PersonaSize,
|
||||||
|
SelectionMode,
|
||||||
|
Selection
|
||||||
} from "office-ui-fabric-react";
|
} from "office-ui-fabric-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { render } from "react-dom";
|
import { render } from "react-dom";
|
||||||
import FeedbackIcon from "../images/Feedback.svg";
|
import FeedbackIcon from "../images/Feedback.svg";
|
||||||
import ConnectIcon from "../images/HostedConnectwhite.svg";
|
import ConnectIcon from "../images/HostedConnectwhite.svg";
|
||||||
|
import ChevronRight from "../images/chevron-right.svg";
|
||||||
import "../less/hostedexplorer.less";
|
import "../less/hostedexplorer.less";
|
||||||
import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent";
|
||||||
import "./Explorer/Menus/NavBar/MeControlComponent.less";
|
import "./Explorer/Menus/NavBar/MeControlComponent.less";
|
||||||
@@ -22,6 +28,7 @@ import "./Shared/appInsights";
|
|||||||
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
|
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
|
||||||
import { AuthContext, AuthProvider } from "./contexts/authContext";
|
import { AuthContext, AuthProvider } from "./contexts/authContext";
|
||||||
import { usePortalAccessToken } from "./hooks/usePortalAccessToken";
|
import { usePortalAccessToken } from "./hooks/usePortalAccessToken";
|
||||||
|
import { useDirectories } from "./hooks/useDirectories";
|
||||||
import { AuthType } from "./AuthType";
|
import { AuthType } from "./AuthType";
|
||||||
|
|
||||||
initializeIcons();
|
initializeIcons();
|
||||||
@@ -31,11 +38,41 @@ const App: React.FunctionComponent = () => {
|
|||||||
const encryptedToken = params && params.get("key");
|
const encryptedToken = params && params.get("key");
|
||||||
const encryptedTokenMetadata = usePortalAccessToken(encryptedToken);
|
const encryptedTokenMetadata = usePortalAccessToken(encryptedToken);
|
||||||
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
|
const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false);
|
||||||
const { isLoggedIn, aadlogin: login, account, aadlogout: logout } = React.useContext(AuthContext);
|
const { isLoggedIn, aadlogin: login, account, aadlogout: logout, tenantId } = React.useContext(AuthContext);
|
||||||
const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false);
|
const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false);
|
||||||
const photo = useGraphPhoto();
|
const photo = useGraphPhoto();
|
||||||
|
const directories = useDirectories();
|
||||||
|
// const [selectedItem, setSelectedItem] = React.useState<any>(undefined);
|
||||||
|
const selection = new Selection({
|
||||||
|
getKey: item => item.tenantId,
|
||||||
|
items: directories,
|
||||||
|
onSelectionChanged: () => {
|
||||||
|
const selected = selection.getSelection()[0];
|
||||||
|
if (selected.tenantId !== tenantId) {
|
||||||
|
console.log("new Tenant", selected.tenantId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectionMode: SelectionMode.single
|
||||||
|
});
|
||||||
|
selection.setKeySelected(tenantId, true, false);
|
||||||
|
|
||||||
const menuProps = {
|
// private _renderPersonaComponent = (): JSX.Element => {
|
||||||
|
// const { user } = this.props;
|
||||||
|
// const personaProps: IPersonaSharedProps = {
|
||||||
|
// imageUrl: user.imageUrl,
|
||||||
|
// text: user.name,
|
||||||
|
// secondaryText: user.email,
|
||||||
|
// showSecondaryText: true,
|
||||||
|
// showInitialsUntilImageLoads: true,
|
||||||
|
// initialsColor: PersonaInitialsColor.teal,
|
||||||
|
// size: PersonaSize.size72,
|
||||||
|
// className: "mecontrolContextualMenuPersona"
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return <Persona {...personaProps} />;
|
||||||
|
// };
|
||||||
|
|
||||||
|
const menuProps: IContextualMenuProps = {
|
||||||
className: "mecontrolContextualMenu",
|
className: "mecontrolContextualMenu",
|
||||||
isBeakVisible: false,
|
isBeakVisible: false,
|
||||||
directionalHintFixed: true,
|
directionalHintFixed: true,
|
||||||
@@ -44,30 +81,15 @@ const App: React.FunctionComponent = () => {
|
|||||||
minPagePadding: 0
|
minPagePadding: 0
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
{
|
|
||||||
key: "Persona",
|
|
||||||
onRender: () => <Persona />
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: "SwitchDirectory",
|
key: "SwitchDirectory",
|
||||||
onRender: () => (
|
text: "Switch Directory",
|
||||||
<div className="switchDirectoryLink" onClick={() => openPanel}>
|
onClick: openPanel
|
||||||
Switch Directory
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "SignOut",
|
key: "SignOut",
|
||||||
onRender: () => (
|
text: "Sign Out",
|
||||||
<div
|
onClick: logout
|
||||||
className="signOutLink"
|
|
||||||
onClick={() => {
|
|
||||||
logout();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Sign out
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -101,8 +123,7 @@ const App: React.FunctionComponent = () => {
|
|||||||
const buttonProps = {
|
const buttonProps = {
|
||||||
id: "mecontrolHeader",
|
id: "mecontrolHeader",
|
||||||
className: "mecontrolHeaderButton",
|
className: "mecontrolHeaderButton",
|
||||||
menuProps: menuProps,
|
menuProps,
|
||||||
onRenderMenuIcon: () => <span />,
|
|
||||||
styles: {
|
styles: {
|
||||||
rootHovered: { backgroundColor: "#393939" },
|
rootHovered: { backgroundColor: "#393939" },
|
||||||
rootFocused: { backgroundColor: "#393939" },
|
rootFocused: { backgroundColor: "#393939" },
|
||||||
@@ -257,14 +278,34 @@ const App: React.FunctionComponent = () => {
|
|||||||
)}
|
)}
|
||||||
<div data-bind="react: firewallWarningComponentAdapter" />
|
<div data-bind="react: firewallWarningComponentAdapter" />
|
||||||
<div data-bind="react: dialogComponentAdapter" />
|
<div data-bind="react: dialogComponentAdapter" />
|
||||||
<Panel headerText="Select Directory" isOpen={isOpen} onDismiss={dismissPanel} closeButtonAriaLabel="Close">
|
<Panel
|
||||||
{/* <div className="directoryDropdownContainer">
|
type={PanelType.medium}
|
||||||
<DefaultDirectoryDropdownComponent />
|
headerText="Select Directory"
|
||||||
</div>
|
isOpen={isOpen}
|
||||||
<div className="directoryDivider" />
|
onDismiss={dismissPanel}
|
||||||
<div className="directoryListContainer">
|
closeButtonAriaLabel="Close"
|
||||||
<DirectoryListComponent />
|
>
|
||||||
</div> */}
|
<DetailsList
|
||||||
|
items={selection.getItems()}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
key: "name",
|
||||||
|
name: "Name",
|
||||||
|
minWidth: 200,
|
||||||
|
maxWidth: 200,
|
||||||
|
fieldName: "displayName"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "id",
|
||||||
|
name: "ID",
|
||||||
|
minWidth: 200,
|
||||||
|
maxWidth: 200,
|
||||||
|
fieldName: "tenantId"
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
selectionMode={SelectionMode.single}
|
||||||
|
selection={selection}
|
||||||
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import Explorer from "./Explorer/Explorer";
|
|||||||
|
|
||||||
export const applyExplorerBindings = (explorer: Explorer) => {
|
export const applyExplorerBindings = (explorer: Explorer) => {
|
||||||
if (!!explorer) {
|
if (!!explorer) {
|
||||||
// This message should ideally be sent immediately after explorer has been initialized for optimal data explorer load times.
|
|
||||||
// TODO: Send another message to describe that the bindings have been applied, and handle message transfers accordingly in the portal
|
|
||||||
sendMessage("ready");
|
|
||||||
window.dataExplorer = explorer;
|
window.dataExplorer = explorer;
|
||||||
BindingHandlersRegisterer.registerBindingHandlers();
|
BindingHandlersRegisterer.registerBindingHandlers();
|
||||||
ko.applyBindings(explorer);
|
ko.applyBindings(explorer);
|
||||||
|
// This message should ideally be sent immediately after explorer has been initialized for optimal data explorer load times.
|
||||||
|
// TODO: Send another message to describe that the bindings have been applied, and handle message transfers accordingly in the portal
|
||||||
|
sendMessage("ready");
|
||||||
$("#divExplorer").show();
|
$("#divExplorer").show();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ interface AuthContext {
|
|||||||
account?: Msal.Account;
|
account?: Msal.Account;
|
||||||
graphToken?: string;
|
graphToken?: string;
|
||||||
armToken?: string;
|
armToken?: string;
|
||||||
|
tenantId?: string;
|
||||||
aadlogout: () => unknown;
|
aadlogout: () => unknown;
|
||||||
aadlogin: () => unknown;
|
aadlogin: () => unknown;
|
||||||
}
|
}
|
||||||
@@ -37,15 +38,22 @@ export const AuthProvider: React.FunctionComponent = ({ children }) => {
|
|||||||
const [account, setAccount] = useState<Msal.Account>();
|
const [account, setAccount] = useState<Msal.Account>();
|
||||||
const [graphToken, setGraphToken] = useState<string>();
|
const [graphToken, setGraphToken] = useState<string>();
|
||||||
const [armToken, setArmToken] = useState<string>();
|
const [armToken, setArmToken] = useState<string>();
|
||||||
|
const [tenantId, setTenantId] = useState<string>();
|
||||||
|
|
||||||
const aadlogin = useCallback(async () => {
|
const aadlogin = useCallback(async () => {
|
||||||
const response = await msal.loginPopup();
|
const response = await msal.loginPopup();
|
||||||
setLoggedIn();
|
setLoggedIn();
|
||||||
setAccount(response.account);
|
setAccount(response.account);
|
||||||
|
setTenantId(response.tenantId);
|
||||||
|
msal.authority = "https://login.microsoftonline.com/481f23b0-3fb3-4e76-812d-15513d11dbfc";
|
||||||
|
|
||||||
const [graphTokenResponse, armTokenResponse] = await Promise.all([
|
const [graphTokenResponse, armTokenResponse] = await Promise.all([
|
||||||
msal.acquireTokenSilent({ scopes: ["https://graph.windows.net//.default"] }),
|
msal.acquireTokenSilent({
|
||||||
msal.acquireTokenSilent({ scopes: ["https://management.azure.com//.default"] })
|
scopes: ["https://graph.windows.net//.default"]
|
||||||
|
}),
|
||||||
|
msal.acquireTokenSilent({
|
||||||
|
scopes: ["https://management.azure.com//.default"]
|
||||||
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setGraphToken(graphTokenResponse.accessToken);
|
setGraphToken(graphTokenResponse.accessToken);
|
||||||
@@ -58,7 +66,7 @@ export const AuthProvider: React.FunctionComponent = ({ children }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{ isLoggedIn, account, aadlogin, aadlogout, graphToken, armToken }}>
|
<AuthContext.Provider value={{ isLoggedIn, account, aadlogin, aadlogout, graphToken, armToken, tenantId }}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
42
src/hooks/useDirectories.tsx
Normal file
42
src/hooks/useDirectories.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
import { AuthContext } from "../contexts/authContext";
|
||||||
|
import { Tenant } from "../Contracts/DataModels";
|
||||||
|
|
||||||
|
interface TenantListResult {
|
||||||
|
nextLink: string;
|
||||||
|
value: Tenant[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchDirectories(accessToken: string): Promise<Tenant[]> {
|
||||||
|
const headers = new Headers();
|
||||||
|
const bearer = `Bearer ${accessToken}`;
|
||||||
|
|
||||||
|
headers.append("Authorization", bearer);
|
||||||
|
|
||||||
|
let tenents: Array<Tenant> = [];
|
||||||
|
let nextLink = `https://management.azure.com/tenants?api-version=2020-01-01`;
|
||||||
|
|
||||||
|
while (nextLink) {
|
||||||
|
const response = await fetch(nextLink, { headers });
|
||||||
|
const result: TenantListResult =
|
||||||
|
response.status === 204 || response.status === 304 ? undefined : await response.json();
|
||||||
|
if (!response.ok) {
|
||||||
|
throw result;
|
||||||
|
}
|
||||||
|
nextLink = result.nextLink;
|
||||||
|
tenents = [...tenents, ...result.value];
|
||||||
|
}
|
||||||
|
return tenents;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDirectories(): Tenant[] {
|
||||||
|
const { armToken } = useContext(AuthContext);
|
||||||
|
const [state, setState] = useState<Tenant[]>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (armToken) {
|
||||||
|
fetchDirectories(armToken).then(response => setState(response));
|
||||||
|
}
|
||||||
|
}, [armToken]);
|
||||||
|
return state || [];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user