created-admin-menu-and-extracted-virtual-machines-to-separated-components
authorPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Fri, 7 Dec 2018 15:05:19 +0000 (16:05 +0100)
committerPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Fri, 7 Dec 2018 15:05:19 +0000 (16:05 +0100)
Feature #14566

Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>

12 files changed:
src/routes/route-change-handlers.ts
src/routes/routes.ts
src/store/navigation/navigation-action.ts
src/store/users/users-actions.ts
src/store/virtual-machines/virtual-machines-actions.ts
src/views-components/main-app-bar/account-menu.tsx
src/views-components/main-app-bar/admin-menu.tsx [new file with mode: 0644]
src/views-components/main-app-bar/main-app-bar.tsx
src/views-components/main-content-bar/main-content-bar.tsx
src/views/virtual-machine-panel/virtual-machine-admin-panel.tsx [new file with mode: 0644]
src/views/virtual-machine-panel/virtual-machine-user-panel.tsx [moved from src/views/virtual-machine-panel/virtual-machine-panel.tsx with 55% similarity]
src/views/workbench/workbench.tsx

index e2454d63ac1aea6eaa6d1ac0fcba9dc0e384dd4e..e68c3c6c1fee2e65bb51043f5123edceb85df407 100644 (file)
@@ -26,7 +26,8 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
     const searchResultsMatch = Routes.matchSearchResultsRoute(pathname);
     const sharedWithMeMatch = Routes.matchSharedWithMeRoute(pathname);
     const runProcessMatch = Routes.matchRunProcessRoute(pathname);
-    const virtualMachineMatch = Routes.matchVirtualMachineRoute(pathname);
+    const virtualMachineUserMatch = Routes.matchUserVirtualMachineRoute(pathname);
+    const virtualMachineAdminMatch = Routes.matchAdminVirtualMachineRoute(pathname);
     const workflowMatch = Routes.matchWorkflowRoute(pathname);
     const sshKeysMatch = Routes.matchSshKeysRoute(pathname);
     const keepServicesMatch = Routes.matchKeepServicesRoute(pathname);
@@ -57,9 +58,11 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
         store.dispatch(WorkbenchActions.loadWorkflow);
     } else if (searchResultsMatch) {
         store.dispatch(WorkbenchActions.loadSearchResults);
-    } else if (virtualMachineMatch) {
+    } else if (virtualMachineUserMatch) {
         store.dispatch(WorkbenchActions.loadVirtualMachines);
-    } else if(repositoryMatch) {
+    } else if (virtualMachineAdminMatch) {
+        store.dispatch(WorkbenchActions.loadVirtualMachines);
+    } else if (repositoryMatch) {
         store.dispatch(WorkbenchActions.loadRepositories);
     } else if (sshKeysMatch) {
         store.dispatch(WorkbenchActions.loadSshKeys);
@@ -71,7 +74,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
         store.dispatch(WorkbenchActions.loadApiClientAuthorizations);
     } else if (myAccountMatch) {
         store.dispatch(WorkbenchActions.loadMyAccount);
-    }else if (userMatch) {
+    } else if (userMatch) {
         store.dispatch(WorkbenchActions.loadUsers);
     }
 };
index 88dfd46951945928336079381035e70e1733ae07..a5a0af1b3b429e0fd7bb62b682f5ed0af0074782 100644 (file)
@@ -19,7 +19,8 @@ export const Routes = {
     REPOSITORIES: '/repositories',
     SHARED_WITH_ME: '/shared-with-me',
     RUN_PROCESS: '/run-process',
-    VIRTUAL_MACHINES: '/virtual-machines',
+    VIRTUAL_MACHINES_USER: '/virtual-machines-user',
+    VIRTUAL_MACHINES_ADMIN: '/virtual-machines-admin',
     WORKFLOWS: '/workflows',
     SEARCH_RESULTS: '/search-results',
     SSH_KEYS: `/ssh-keys`,
@@ -85,8 +86,11 @@ export const matchWorkflowRoute = (route: string) =>
 export const matchSearchResultsRoute = (route: string) =>
     matchPath<ResourceRouteParams>(route, { path: Routes.SEARCH_RESULTS });
 
-export const matchVirtualMachineRoute = (route: string) =>
-    matchPath<ResourceRouteParams>(route, { path: Routes.VIRTUAL_MACHINES });
+export const matchUserVirtualMachineRoute = (route: string) =>
+    matchPath<ResourceRouteParams>(route, { path: Routes.VIRTUAL_MACHINES_USER });
+
+export const matchAdminVirtualMachineRoute = (route: string) =>
+    matchPath<ResourceRouteParams>(route, { path: Routes.VIRTUAL_MACHINES_ADMIN });
 
 export const matchRepositoriesRoute = (route: string) =>
     matchPath<ResourceRouteParams>(route, { path: Routes.REPOSITORIES });
index 8d68a4b6856e4179260a95315eac5158f4c8a95f..d070fd0f4181608bd860f50348348966d2c5821a 100644 (file)
@@ -62,7 +62,9 @@ export const navigateToRunProcess = push(Routes.RUN_PROCESS);
 
 export const navigateToSearchResults = push(Routes.SEARCH_RESULTS);
 
-export const navigateToVirtualMachines = push(Routes.VIRTUAL_MACHINES);
+export const navigateToUserVirtualMachines = push(Routes.VIRTUAL_MACHINES_USER);
+
+export const navigateToAdminVirtualMachines = push(Routes.VIRTUAL_MACHINES_ADMIN);
 
 export const navigateToRepositories = push(Routes.REPOSITORIES);
 
index 64c3646a36ae70be55af9470d1ad14ab43eee719..51c9ba0b26ce380eb0add6da7138d47ee41aacf9 100644 (file)
@@ -7,8 +7,7 @@ import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-act
 import { RootState } from '~/store/store';
 import { ServiceRepository } from "~/services/services";
 import { dialogActions } from '~/store/dialog/dialog-actions';
-import { startSubmit, reset, stopSubmit } from "redux-form";
-import { getCommonResourceServiceError, CommonResourceServiceError } from "~/services/common-service/common-resource-service";
+import { startSubmit, reset } from "redux-form";
 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
 import { UserResource } from "~/models/user";
 import { getResource } from '~/store/resources/resources';
@@ -16,7 +15,7 @@ import { navigateToProject } from "~/store/navigation/navigation-action";
 
 export const USERS_PANEL_ID = 'usersPanel';
 export const USER_ATTRIBUTES_DIALOG = 'userAttributesDialog';
-export const USER_CREATE_FORM_NAME = 'repositoryCreateFormName';
+export const USER_CREATE_FORM_NAME = 'userCreateFormName';
 
 export interface UserCreateFormDialogData {
     email: string;
index 0aaa9050d7276cd4eef159b53166d5a34032182b..ea6d1aff76f25b95626ed778d3bd85e02041026b 100644 (file)
@@ -5,7 +5,7 @@
 import { Dispatch } from "redux";
 import { RootState } from '~/store/store';
 import { ServiceRepository } from "~/services/services";
-import { navigateToVirtualMachines } from "../navigation/navigation-action";
+import { navigateToUserVirtualMachines, navigateToAdminVirtualMachines } from "~/store/navigation/navigation-action";
 import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-action';
 import { formatDate } from "~/common/formatters";
 import { unionize, ofType, UnionOf } from "~/common/unionize";
@@ -28,9 +28,14 @@ export const VIRTUAL_MACHINES_PANEL = 'virtualMachinesPanel';
 export const VIRTUAL_MACHINE_ATTRIBUTES_DIALOG = 'virtualMachineAttributesDialog';
 export const VIRTUAL_MACHINE_REMOVE_DIALOG = 'virtualMachineRemoveDialog';
 
-export const openVirtualMachines = () =>
+export const openUserVirtualMachines = () =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        dispatch<any>(navigateToVirtualMachines);
+        dispatch<any>(navigateToUserVirtualMachines);
+    };
+
+export const openAdminVirtualMachines = () =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch<any>(navigateToAdminVirtualMachines);
     };
 
 export const openVirtualMachineAttributes = (uuid: string) =>
@@ -45,7 +50,16 @@ const loadRequestedDate = () =>
         dispatch(virtualMachinesActions.SET_REQUESTED_DATE(date));
     };
 
-export const loadVirtualMachinesData = () =>
+export const loadVirtualMachinesAdminData = () =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch<any>(loadRequestedDate());
+        const virtualMachines = await services.virtualMachineService.list();
+        dispatch(virtualMachinesActions.SET_VIRTUAL_MACHINES(virtualMachines));
+        const getAllLogins = await services.virtualMachineService.getAllLogins();
+        dispatch(virtualMachinesActions.SET_LOGINS(getAllLogins));
+    };
+
+export const loadVirtualMachinesUserData = () =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         dispatch<any>(loadRequestedDate());
         const virtualMachines = await services.virtualMachineService.list();
@@ -57,8 +71,6 @@ export const loadVirtualMachinesData = () =>
         });
         dispatch(virtualMachinesActions.SET_VIRTUAL_MACHINES(virtualMachines));
         dispatch(virtualMachinesActions.SET_LINKS(links));
-        const getAllLogins = await services.virtualMachineService.getAllLogins();
-        dispatch(virtualMachinesActions.SET_LOGINS(getAllLogins));
     };
 
 export const saveRequestedDate = () =>
@@ -86,7 +98,7 @@ export const removeVirtualMachine = (uuid: string) =>
         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...' }));
         await services.virtualMachineService.delete(uuid);
         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
-        dispatch<any>(loadVirtualMachinesData());
+        dispatch<any>(loadVirtualMachinesAdminData());
     };
 
 const virtualMachinesBindedActions = bindDataExplorerActions(VIRTUAL_MACHINES_PANEL);
index 44b113df39e6b33a347bd661680d4af3312dae04..31a28ecfba9fecd78f952b231c0d1686463c5e01 100644 (file)
@@ -12,12 +12,8 @@ import { logout } from '~/store/auth/auth-action';
 import { RootState } from "~/store/store";
 import { openCurrentTokenDialog } from '~/store/current-token-dialog/current-token-dialog-actions';
 import { openRepositoriesPanel } from "~/store/repositories/repositories-actions";
-import { 
-    navigateToSshKeys, navigateToKeepServices, navigateToComputeNodes,
-    navigateToApiClientAuthorizations, navigateToMyAccount
-} from '~/store/navigation/navigation-action';
-import { openVirtualMachines } from "~/store/virtual-machines/virtual-machines-actions";
-import { navigateToUsers } from '~/store/navigation/navigation-action';
+import { navigateToSshKeys, navigateToMyAccount } from '~/store/navigation/navigation-action';
+import { openUserVirtualMachines } from "~/store/virtual-machines/virtual-machines-actions";
 
 interface AccountMenuProps {
     user?: User;
@@ -37,14 +33,10 @@ export const AccountMenu = connect(mapStateToProps)(
                 <MenuItem>
                     {getUserFullname(user)}
                 </MenuItem>
-                <MenuItem onClick={() => dispatch(openVirtualMachines())}>Virtual Machines</MenuItem>
-                <MenuItem onClick={() => dispatch(openRepositoriesPanel())}>Repositories</MenuItem>
+                <MenuItem onClick={() => dispatch(openUserVirtualMachines())}>Virtual Machines</MenuItem>
+                {!user.isAdmin && <MenuItem onClick={() => dispatch(openRepositoriesPanel())}>Repositories</MenuItem>}
                 <MenuItem onClick={() => dispatch(openCurrentTokenDialog)}>Current token</MenuItem>
                 <MenuItem onClick={() => dispatch(navigateToSshKeys)}>Ssh Keys</MenuItem>
-                <MenuItem onClick={() => dispatch(navigateToUsers)}>Users</MenuItem>
-                { user.isAdmin && <MenuItem onClick={() => dispatch(navigateToApiClientAuthorizations)}>Api Tokens</MenuItem> }
-                { user.isAdmin && <MenuItem onClick={() => dispatch(navigateToKeepServices)}>Keep Services</MenuItem> }
-                { user.isAdmin && <MenuItem onClick={() => dispatch(navigateToComputeNodes)}>Compute Nodes</MenuItem> }
                 <MenuItem onClick={() => dispatch(navigateToMyAccount)}>My account</MenuItem>
                 <MenuItem onClick={() => dispatch(logout())}>Logout</MenuItem>
             </DropdownMenu>
diff --git a/src/views-components/main-app-bar/admin-menu.tsx b/src/views-components/main-app-bar/admin-menu.tsx
new file mode 100644 (file)
index 0000000..f754c85
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { MenuItem } from "@material-ui/core";
+import { User } from "~/models/user";
+import { DropdownMenu } from "~/components/dropdown-menu/dropdown-menu";
+import { ShareMeIcon } from "~/components/icon/icon";
+import { DispatchProp, connect } from 'react-redux';
+import { logout } from '~/store/auth/auth-action';
+import { RootState } from "~/store/store";
+import { openRepositoriesPanel } from "~/store/repositories/repositories-actions";
+import { 
+    navigateToSshKeys, navigateToKeepServices, navigateToComputeNodes,
+    navigateToApiClientAuthorizations
+} from '~/store/navigation/navigation-action';
+import { openAdminVirtualMachines } from "~/store/virtual-machines/virtual-machines-actions";
+import { navigateToUsers } from '~/store/navigation/navigation-action';
+
+interface AdminMenuProps {
+    user?: User;
+}
+
+const mapStateToProps = (state: RootState): AdminMenuProps => ({
+    user: state.auth.user
+});
+
+export const AdminMenu = connect(mapStateToProps)(
+    ({ user, dispatch }: AdminMenuProps & DispatchProp<any>) =>
+        user
+            ? <DropdownMenu
+                icon={<ShareMeIcon />}
+                id="admin-menu"
+                title="Admin Panel">
+                <MenuItem onClick={() => dispatch(openRepositoriesPanel())}>Repositories</MenuItem>
+                <MenuItem onClick={() => dispatch(openAdminVirtualMachines())}>Virtual Machines</MenuItem>
+                <MenuItem onClick={() => dispatch(navigateToSshKeys)}>Ssh Keys</MenuItem>
+                <MenuItem onClick={() => dispatch(navigateToApiClientAuthorizations)}>Api Tokens</MenuItem>
+                <MenuItem onClick={() => dispatch(navigateToUsers)}>Users</MenuItem>
+                <MenuItem onClick={() => dispatch(navigateToComputeNodes)}>Compute Nodes</MenuItem>
+                <MenuItem onClick={() => dispatch(navigateToKeepServices)}>Keep Services</MenuItem>
+                <MenuItem onClick={() => dispatch(logout())}>Logout</MenuItem>
+            </DropdownMenu>
+            : null);
index b936fb14d3fbc05412bc76178efbf466fa5dc496..8a7e9f2000f0af954275b3ac6717ff6479550ce1 100644 (file)
@@ -11,8 +11,9 @@ import { SearchBar } from "~/views-components/search-bar/search-bar";
 import { Routes } from '~/routes/routes';
 import { NotificationsMenu } from "~/views-components/main-app-bar/notifications-menu";
 import { AccountMenu } from "~/views-components/main-app-bar/account-menu";
-import { HelpMenu } from './help-menu';
+import { HelpMenu } from '~/views-components/main-app-bar/help-menu';
 import { ReactNode } from "react";
+import { AdminMenu } from "~/views-components/main-app-bar/admin-menu";
 
 type CssRules = 'toolbar' | 'link';
 
@@ -65,6 +66,7 @@ export const MainAppBar = withStyles(styles)(
                             ? <>
                                 <NotificationsMenu />
                                 <AccountMenu />
+                                {props.user.isAdmin && <AdminMenu />}
                                 <HelpMenu />
                             </>
                             : <HelpMenu />}
index 8c2e4ceda26a8f029b78492747942f86b92c1a17..944f135ab7c083e03efac83ef5d532738c8efeb3 100644 (file)
@@ -18,10 +18,11 @@ interface MainContentBarProps {
 
 const isButtonVisible = ({ router }: RootState) => {
     const pathname = router.location ? router.location.pathname : '';
-    return !Routes.matchWorkflowRoute(pathname) && !Routes.matchVirtualMachineRoute(pathname) &&
-        !Routes.matchRepositoriesRoute(pathname) && !Routes.matchSshKeysRoute(pathname) &&
-        !Routes.matchKeepServicesRoute(pathname) && !Routes.matchComputeNodesRoute(pathname) &&
-        !Routes.matchApiClientAuthorizationsRoute(pathname) && !Routes.matchUsersRoute(pathname);
+    return !Routes.matchWorkflowRoute(pathname) && !Routes.matchUserVirtualMachineRoute(pathname) &&
+        !Routes.matchAdminVirtualMachineRoute(pathname) && !Routes.matchRepositoriesRoute(pathname) && 
+        !Routes.matchSshKeysRoute(pathname) && !Routes.matchKeepServicesRoute(pathname) && 
+        !Routes.matchComputeNodesRoute(pathname) && !Routes.matchApiClientAuthorizationsRoute(pathname) && 
+        !Routes.matchUsersRoute(pathname) && !Routes.matchMyAccountRoute(pathname);
 };
 
 export const MainContentBar = connect((state: RootState) => ({
diff --git a/src/views/virtual-machine-panel/virtual-machine-admin-panel.tsx b/src/views/virtual-machine-panel/virtual-machine-admin-panel.tsx
new file mode 100644 (file)
index 0000000..dda2889
--- /dev/null
@@ -0,0 +1,112 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { Grid, Card, CardContent, TableBody, TableCell, TableHead, TableRow, Table, Tooltip, IconButton } from '@material-ui/core';
+import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { compose, Dispatch } from 'redux';
+import { loadVirtualMachinesAdminData } from '~/store/virtual-machines/virtual-machines-actions';
+import { RootState } from '~/store/store';
+import { ListResults } from '~/services/common-service/common-service';
+import { MoreOptionsIcon } from '~/components/icon/icon';
+import { VirtualMachineLogins, VirtualMachinesResource } from '~/models/virtual-machines';
+import { openVirtualMachinesContextMenu } from '~/store/context-menu/context-menu-actions';
+
+type CssRules = 'moreOptionsButton' | 'moreOptions';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    moreOptionsButton: {
+        padding: 0
+    },
+    moreOptions: {
+        textAlign: 'right',
+        '&:last-child': {
+            paddingRight: 0
+        }
+    },
+});
+
+const mapStateToProps = ({ virtualMachines }: RootState) => {
+    return {
+        logins: virtualMachines.logins,
+        ...virtualMachines
+    };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch): Pick<VirtualMachinesPanelActionProps, 'loadVirtualMachinesData' | 'onOptionsMenuOpen'> => ({
+    loadVirtualMachinesData: () => dispatch<any>(loadVirtualMachinesAdminData()),
+    onOptionsMenuOpen: (event, virtualMachine) => {
+        dispatch<any>(openVirtualMachinesContextMenu(event, virtualMachine));
+    },
+});
+
+interface VirtualMachinesPanelDataProps {
+    virtualMachines: ListResults<any>;
+    logins: VirtualMachineLogins;
+}
+
+interface VirtualMachinesPanelActionProps {
+    loadVirtualMachinesData: () => string;
+    onOptionsMenuOpen: (event: React.MouseEvent<HTMLElement>, virtualMachine: VirtualMachinesResource) => void;
+}
+
+type VirtualMachineProps = VirtualMachinesPanelActionProps & VirtualMachinesPanelDataProps & WithStyles<CssRules>;
+
+export const VirtualMachineAdminPanel = compose(
+    withStyles(styles),
+    connect(mapStateToProps, mapDispatchToProps))(
+        class extends React.Component<VirtualMachineProps> {
+            componentDidMount() {
+                this.props.loadVirtualMachinesData();
+            }
+
+            render() {
+                const { virtualMachines } = this.props;
+                return (
+                    <Grid container spacing={16}>
+                        {virtualMachines.itemsAvailable > 0 && <CardContentWithVirtualMachines {...this.props} />}
+                    </Grid>
+                );
+            }
+        }
+    );
+
+const CardContentWithVirtualMachines = (props: VirtualMachineProps) =>
+    <Grid item xs={12}>
+        <Card>
+            <CardContent>
+                {virtualMachinesTable(props)}
+            </CardContent>
+        </Card>
+    </Grid>;
+
+const virtualMachinesTable = (props: VirtualMachineProps) =>
+    <Table>
+        <TableHead>
+            <TableRow>
+                <TableCell>Uuid</TableCell>
+                <TableCell>Host name</TableCell>
+                <TableCell>Logins</TableCell>
+                <TableCell />
+            </TableRow>
+        </TableHead>
+        <TableBody>
+            {props.logins.items.length > 0 && props.virtualMachines.items.map((it, index) =>
+                <TableRow key={index}>
+                    <TableCell>{it.uuid}</TableCell>
+                    <TableCell>{it.hostname}</TableCell>
+                    <TableCell>["{props.logins.items[0].username}"]</TableCell>
+                    <TableCell className={props.classes.moreOptions}>
+                        <Tooltip title="More options" disableFocusListener>
+                            <IconButton onClick={event => props.onOptionsMenuOpen(event, it)} className={props.classes.moreOptionsButton}>
+                                <MoreOptionsIcon />
+                            </IconButton>
+                        </Tooltip>
+                    </TableCell>
+                </TableRow>
+            )}
+        </TableBody>
+    </Table>;
similarity index 55%
rename from src/views/virtual-machine-panel/virtual-machine-panel.tsx
rename to src/views/virtual-machine-panel/virtual-machine-user-panel.tsx
index d5d345551c41a680a26c6b62247f2109980567ce..175630530cb0b99c595d3e5e122fa72ca9b52af8 100644 (file)
@@ -4,21 +4,19 @@
 
 import * as React from 'react';
 import { connect } from 'react-redux';
-import { Grid, Typography, Button, Card, CardContent, TableBody, TableCell, TableHead, TableRow, Table, Tooltip, IconButton } from '@material-ui/core';
+import { Grid, Typography, Button, Card, CardContent, TableBody, TableCell, TableHead, TableRow, Table, Tooltip } from '@material-ui/core';
 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { DefaultCodeSnippet } from '~/components/default-code-snippet/default-code-snippet';
 import { Link } from 'react-router-dom';
 import { compose, Dispatch } from 'redux';
-import { saveRequestedDate, loadVirtualMachinesData } from '~/store/virtual-machines/virtual-machines-actions';
+import { saveRequestedDate, loadVirtualMachinesUserData } from '~/store/virtual-machines/virtual-machines-actions';
 import { RootState } from '~/store/store';
 import { ListResults } from '~/services/common-service/common-service';
-import { HelpIcon, MoreOptionsIcon } from '~/components/icon/icon';
-import { VirtualMachineLogins, VirtualMachinesResource } from '~/models/virtual-machines';
+import { HelpIcon } from '~/components/icon/icon';
 import { Routes } from '~/routes/routes';
-import { openVirtualMachinesContextMenu } from '~/store/context-menu/context-menu-actions';
 
-type CssRules = 'button' | 'codeSnippet' | 'link' | 'linkIcon' | 'rightAlign' | 'cardWithoutMachines' | 'icon' | 'moreOptionsButton' | 'moreOptions';
+type CssRules = 'button' | 'codeSnippet' | 'link' | 'linkIcon' | 'rightAlign' | 'cardWithoutMachines' | 'icon';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     button: {
@@ -56,52 +54,35 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     icon: {
         textAlign: "right",
         marginTop: theme.spacing.unit
-    },
-    moreOptionsButton: {
-        padding: 0
-    },
-    moreOptions: {
-        textAlign: 'right',
-        '&:last-child': {
-            paddingRight: 0
-        }
-    },
+    }
 });
 
-const mapStateToProps = ({ virtualMachines, auth }: RootState) => {
+const mapStateToProps = ({ virtualMachines }: RootState) => {
     return {
         requestedDate: virtualMachines.date,
-        isAdmin: auth.user!.isAdmin,
-        logins: virtualMachines.logins,
         ...virtualMachines
     };
 };
 
-const mapDispatchToProps = (dispatch: Dispatch): Pick<VirtualMachinesPanelActionProps, 'loadVirtualMachinesData' | 'saveRequestedDate' | 'onOptionsMenuOpen'> => ({
+const mapDispatchToProps = (dispatch: Dispatch): Pick<VirtualMachinesPanelActionProps, 'loadVirtualMachinesData' | 'saveRequestedDate'> => ({
     saveRequestedDate: () => dispatch<any>(saveRequestedDate()),
-    loadVirtualMachinesData: () => dispatch<any>(loadVirtualMachinesData()),
-    onOptionsMenuOpen: (event, virtualMachine) => {
-        dispatch<any>(openVirtualMachinesContextMenu(event, virtualMachine));
-    },
+    loadVirtualMachinesData: () => dispatch<any>(loadVirtualMachinesUserData()),
 });
 
 interface VirtualMachinesPanelDataProps {
     requestedDate: string;
     virtualMachines: ListResults<any>;
-    logins: VirtualMachineLogins;
     links: ListResults<any>;
-    isAdmin: boolean;
 }
 
 interface VirtualMachinesPanelActionProps {
     saveRequestedDate: () => void;
     loadVirtualMachinesData: () => string;
-    onOptionsMenuOpen: (event: React.MouseEvent<HTMLElement>, virtualMachine: VirtualMachinesResource) => void;
 }
 
 type VirtualMachineProps = VirtualMachinesPanelActionProps & VirtualMachinesPanelDataProps & WithStyles<CssRules>;
 
-export const VirtualMachinePanel = compose(
+export const VirtualMachineUserPanel = compose(
     withStyles(styles),
     connect(mapStateToProps, mapDispatchToProps))(
         class extends React.Component<VirtualMachineProps> {
@@ -110,19 +91,19 @@ export const VirtualMachinePanel = compose(
             }
 
             render() {
-                const { virtualMachines, links, isAdmin } = this.props;
+                const { virtualMachines, links } = this.props;
                 return (
                     <Grid container spacing={16}>
-                        {!isAdmin && virtualMachines.itemsAvailable > 0 && <CardContentWithNoVirtualMachines {...this.props} />}
+                        {virtualMachines.itemsAvailable === 0 && <CardContentWithoutVirtualMachines {...this.props} />}
                         {virtualMachines.itemsAvailable > 0 && links.itemsAvailable > 0 && <CardContentWithVirtualMachines {...this.props} />}
-                        {!isAdmin && <CardSSHSection {...this.props} />}
+                        {<CardSSHSection {...this.props} />}
                     </Grid>
                 );
             }
         }
     );
 
-const CardContentWithNoVirtualMachines = (props: VirtualMachineProps) =>
+const CardContentWithoutVirtualMachines = (props: VirtualMachineProps) =>
     <Grid item xs={12}>
         <Card>
             <CardContent className={props.classes.cardWithoutMachines}>
@@ -132,13 +113,7 @@ const CardContentWithNoVirtualMachines = (props: VirtualMachineProps) =>
                     </Typography>
                 </Grid>
                 <Grid item xs={6} className={props.classes.rightAlign}>
-                    <Button variant="contained" color="primary" className={props.classes.button} onClick={props.saveRequestedDate}>
-                        SEND REQUEST FOR SHELL ACCESS
-                    </Button>
-                    {props.requestedDate &&
-                        <Typography variant="body1">
-                            A request for shell access was sent on {props.requestedDate}
-                        </Typography>}
+                    {virtualMachineSendRequest(props)}
                 </Grid>
             </CardContent>
         </Card>
@@ -148,32 +123,36 @@ const CardContentWithVirtualMachines = (props: VirtualMachineProps) =>
     <Grid item xs={12}>
         <Card>
             <CardContent>
-                {props.isAdmin ? <span>{adminVirtualMachinesTable(props)}</span>
-                    : <span>
-                        <div className={props.classes.rightAlign}>
-                            <Button variant="contained" color="primary" className={props.classes.button} onClick={props.saveRequestedDate}>
-                                SEND REQUEST FOR SHELL ACCESS
-                                </Button>
-                            {props.requestedDate &&
-                                <Typography variant="body1">
-                                    A request for shell access was sent on {props.requestedDate}
-                                </Typography>}
-                        </div>
-                        <div className={props.classes.icon}>
-                            <a href="https://doc.arvados.org/user/getting_started/vm-login-with-webshell.html" target="_blank" className={props.classes.linkIcon}>
-                                <Tooltip title="Access VM using webshell">
-                                    <HelpIcon />
-                                </Tooltip>
-                            </a>
-                        </div>
-                        {userVirtualMachinesTable(props)}
-                    </span>
-                }
+                <span>
+                    <div className={props.classes.rightAlign}>
+                        {virtualMachineSendRequest(props)}
+                    </div>
+                    <div className={props.classes.icon}>
+                        <a href="https://doc.arvados.org/user/getting_started/vm-login-with-webshell.html" target="_blank" className={props.classes.linkIcon}>
+                            <Tooltip title="Access VM using webshell">
+                                <HelpIcon />
+                            </Tooltip>
+                        </a>
+                    </div>
+                    {virtualMachinesTable(props)}
+                </span>
+
             </CardContent>
         </Card>
     </Grid>;
 
-const userVirtualMachinesTable = (props: VirtualMachineProps) =>
+const virtualMachineSendRequest = (props: VirtualMachineProps) =>
+    <span>
+        <Button variant="contained" color="primary" className={props.classes.button} onClick={props.saveRequestedDate}>
+            SEND REQUEST FOR SHELL ACCESS
+        </Button>
+        {props.requestedDate &&
+            <Typography variant="body1">
+                A request for shell access was sent on {props.requestedDate}
+            </Typography>}
+    </span>;
+
+const virtualMachinesTable = (props: VirtualMachineProps) =>
     <Table>
         <TableHead>
             <TableRow>
@@ -199,34 +178,6 @@ const userVirtualMachinesTable = (props: VirtualMachineProps) =>
         </TableBody>
     </Table>;
 
-const adminVirtualMachinesTable = (props: VirtualMachineProps) =>
-    <Table>
-        <TableHead>
-            <TableRow>
-                <TableCell>Uuid</TableCell>
-                <TableCell>Host name</TableCell>
-                <TableCell>Logins</TableCell>
-                <TableCell />
-            </TableRow>
-        </TableHead>
-        <TableBody>
-            {props.logins.items.length > 0 && props.virtualMachines.items.map((it, index) =>
-                <TableRow key={index}>
-                    <TableCell>{it.uuid}</TableCell>
-                    <TableCell>{it.hostname}</TableCell>
-                    <TableCell>["{props.logins.items[0].username}"]</TableCell>
-                    <TableCell className={props.classes.moreOptions}>
-                        <Tooltip title="More options" disableFocusListener>
-                            <IconButton onClick={event => props.onOptionsMenuOpen(event, it)} className={props.classes.moreOptionsButton}>
-                                <MoreOptionsIcon />
-                            </IconButton>
-                        </Tooltip>
-                    </TableCell>
-                </TableRow>
-            )}
-        </TableBody>
-    </Table>;
-
 const getUsername = (links: ListResults<any>) => {
     return links.items[0].properties.username;
 };
index 70f2a2ddd207a7303851d542e999d589be24b175..d4832360b7527cb4770586742747b75e08910dd7 100644 (file)
@@ -49,7 +49,8 @@ import { MyAccountPanel } from '~/views/my-account-panel/my-account-panel';
 import { SharingDialog } from '~/views-components/sharing-dialog/sharing-dialog';
 import { AdvancedTabDialog } from '~/views-components/advanced-tab-dialog/advanced-tab-dialog';
 import { ProcessInputDialog } from '~/views-components/process-input-dialog/process-input-dialog';
-import { VirtualMachinePanel } from '~/views/virtual-machine-panel/virtual-machine-panel';
+import { VirtualMachineUserPanel } from '~/views/virtual-machine-panel/virtual-machine-user-panel';
+import { VirtualMachineAdminPanel } from '~/views/virtual-machine-panel/virtual-machine-admin-panel';
 import { ProjectPropertiesDialog } from '~/views-components/project-properties-dialog/project-properties-dialog';
 import { RepositoriesPanel } from '~/views/repositories-panel/repositories-panel';
 import { KeepServicePanel } from '~/views/keep-service-panel/keep-service-panel';
@@ -144,7 +145,8 @@ export const WorkbenchPanel =
                                 <Route path={Routes.RUN_PROCESS} component={RunProcessPanel} />
                                 <Route path={Routes.WORKFLOWS} component={WorkflowPanel} />
                                 <Route path={Routes.SEARCH_RESULTS} component={SearchResultsPanel} />
-                                <Route path={Routes.VIRTUAL_MACHINES} component={VirtualMachinePanel} />
+                                <Route path={Routes.VIRTUAL_MACHINES_USER} component={VirtualMachineUserPanel} />
+                                <Route path={Routes.VIRTUAL_MACHINES_ADMIN} component={VirtualMachineAdminPanel} />
                                 <Route path={Routes.REPOSITORIES} component={RepositoriesPanel} />
                                 <Route path={Routes.SSH_KEYS} component={SshKeyPanel} />
                                 <Route path={Routes.KEEP_SERVICES} component={KeepServicePanel} />