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 {
|
||||
DefaultButton,
|
||||
DetailsList,
|
||||
DirectionalHint,
|
||||
FocusZone,
|
||||
IContextualMenuProps,
|
||||
initializeIcons,
|
||||
Panel,
|
||||
PanelType,
|
||||
Persona,
|
||||
PersonaInitialsColor,
|
||||
PersonaSize
|
||||
PersonaSize,
|
||||
SelectionMode,
|
||||
Selection
|
||||
} from "office-ui-fabric-react";
|
||||
import * as React from "react";
|
||||
import { render } from "react-dom";
|
||||
import FeedbackIcon from "../images/Feedback.svg";
|
||||
import ConnectIcon from "../images/HostedConnectwhite.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";
|
||||
@@ -22,6 +28,7 @@ import "./Shared/appInsights";
|
||||
import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent";
|
||||
import { AuthContext, AuthProvider } from "./contexts/authContext";
|
||||
import { usePortalAccessToken } from "./hooks/usePortalAccessToken";
|
||||
import { useDirectories } from "./hooks/useDirectories";
|
||||
import { AuthType } from "./AuthType";
|
||||
|
||||
initializeIcons();
|
||||
@@ -31,11 +38,41 @@ const App: React.FunctionComponent = () => {
|
||||
const encryptedToken = params && params.get("key");
|
||||
const encryptedTokenMetadata = usePortalAccessToken(encryptedToken);
|
||||
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 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",
|
||||
isBeakVisible: false,
|
||||
directionalHintFixed: true,
|
||||
@@ -44,30 +81,15 @@ const App: React.FunctionComponent = () => {
|
||||
minPagePadding: 0
|
||||
},
|
||||
items: [
|
||||
{
|
||||
key: "Persona",
|
||||
onRender: () => <Persona />
|
||||
},
|
||||
{
|
||||
key: "SwitchDirectory",
|
||||
onRender: () => (
|
||||
<div className="switchDirectoryLink" onClick={() => openPanel}>
|
||||
Switch Directory
|
||||
</div>
|
||||
)
|
||||
text: "Switch Directory",
|
||||
onClick: openPanel
|
||||
},
|
||||
{
|
||||
key: "SignOut",
|
||||
onRender: () => (
|
||||
<div
|
||||
className="signOutLink"
|
||||
onClick={() => {
|
||||
logout();
|
||||
}}
|
||||
>
|
||||
Sign out
|
||||
</div>
|
||||
)
|
||||
text: "Sign Out",
|
||||
onClick: logout
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -101,8 +123,7 @@ const App: React.FunctionComponent = () => {
|
||||
const buttonProps = {
|
||||
id: "mecontrolHeader",
|
||||
className: "mecontrolHeaderButton",
|
||||
menuProps: menuProps,
|
||||
onRenderMenuIcon: () => <span />,
|
||||
menuProps,
|
||||
styles: {
|
||||
rootHovered: { backgroundColor: "#393939" },
|
||||
rootFocused: { backgroundColor: "#393939" },
|
||||
@@ -257,14 +278,34 @@ const App: React.FunctionComponent = () => {
|
||||
)}
|
||||
<div data-bind="react: firewallWarningComponentAdapter" />
|
||||
<div data-bind="react: dialogComponentAdapter" />
|
||||
<Panel headerText="Select Directory" isOpen={isOpen} onDismiss={dismissPanel} closeButtonAriaLabel="Close">
|
||||
{/* <div className="directoryDropdownContainer">
|
||||
<DefaultDirectoryDropdownComponent />
|
||||
</div>
|
||||
<div className="directoryDivider" />
|
||||
<div className="directoryListContainer">
|
||||
<DirectoryListComponent />
|
||||
</div> */}
|
||||
<Panel
|
||||
type={PanelType.medium}
|
||||
headerText="Select Directory"
|
||||
isOpen={isOpen}
|
||||
onDismiss={dismissPanel}
|
||||
closeButtonAriaLabel="Close"
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,12 +5,12 @@ import Explorer from "./Explorer/Explorer";
|
||||
|
||||
export const applyExplorerBindings = (explorer: 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;
|
||||
BindingHandlersRegisterer.registerBindingHandlers();
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ interface AuthContext {
|
||||
account?: Msal.Account;
|
||||
graphToken?: string;
|
||||
armToken?: string;
|
||||
tenantId?: string;
|
||||
aadlogout: () => unknown;
|
||||
aadlogin: () => unknown;
|
||||
}
|
||||
@@ -37,15 +38,22 @@ export const AuthProvider: React.FunctionComponent = ({ children }) => {
|
||||
const [account, setAccount] = useState<Msal.Account>();
|
||||
const [graphToken, setGraphToken] = useState<string>();
|
||||
const [armToken, setArmToken] = useState<string>();
|
||||
const [tenantId, setTenantId] = useState<string>();
|
||||
|
||||
const aadlogin = useCallback(async () => {
|
||||
const response = await msal.loginPopup();
|
||||
setLoggedIn();
|
||||
setAccount(response.account);
|
||||
setTenantId(response.tenantId);
|
||||
msal.authority = "https://login.microsoftonline.com/481f23b0-3fb3-4e76-812d-15513d11dbfc";
|
||||
|
||||
const [graphTokenResponse, armTokenResponse] = await Promise.all([
|
||||
msal.acquireTokenSilent({ scopes: ["https://graph.windows.net//.default"] }),
|
||||
msal.acquireTokenSilent({ scopes: ["https://management.azure.com//.default"] })
|
||||
msal.acquireTokenSilent({
|
||||
scopes: ["https://graph.windows.net//.default"]
|
||||
}),
|
||||
msal.acquireTokenSilent({
|
||||
scopes: ["https://management.azure.com//.default"]
|
||||
})
|
||||
]);
|
||||
|
||||
setGraphToken(graphTokenResponse.accessToken);
|
||||
@@ -58,7 +66,7 @@ export const AuthProvider: React.FunctionComponent = ({ children }) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ isLoggedIn, account, aadlogin, aadlogout, graphToken, armToken }}>
|
||||
<AuthContext.Provider value={{ isLoggedIn, account, aadlogin, aadlogout, graphToken, armToken, tenantId }}>
|
||||
{children}
|
||||
</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