[ARVADOS-WORKBENCH2] created: 1.2.0-874-g4d73bcb
Git user
git at public.curoverse.com
Fri Nov 16 07:00:44 EST 2018
at 4d73bcbaa792da5854f4d266dd7be32fc99e3289 (commit)
commit 4d73bcbaa792da5854f4d266dd7be32fc99e3289
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date: Fri Nov 16 13:00:21 2018 +0100
virtual-machines-panel-init
Feature #13864
Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx
index a0fbd6e..4e1a063 100644
--- a/src/components/icon/icon.tsx
+++ b/src/components/icon/icon.tsx
@@ -3,7 +3,6 @@
// SPDX-License-Identifier: AGPL-3.0
import * as React from 'react';
-import AccessTime from '@material-ui/icons/AccessTime';
import Add from '@material-ui/icons/Add';
import ArrowBack from '@material-ui/icons/ArrowBack';
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
@@ -86,7 +85,6 @@ export const ProcessIcon: IconType = (props) => <BubbleChart {...props} />;
export const ProjectIcon: IconType = (props) => <Folder {...props} />;
export const ProjectsIcon: IconType = (props) => <Inbox {...props} />;
export const ProvenanceGraphIcon: IconType = (props) => <DeviceHub {...props} />;
-export const RecentIcon: IconType = (props) => <AccessTime {...props} />;
export const RemoveIcon: IconType = (props) => <Delete {...props} />;
export const RemoveFavoriteIcon: IconType = (props) => <Star {...props} />;
export const RenameIcon: IconType = (props) => <Edit {...props} />;
diff --git a/src/models/resource.ts b/src/models/resource.ts
index b8156cf..f200713 100644
--- a/src/models/resource.ts
+++ b/src/models/resource.ts
@@ -29,6 +29,7 @@ export enum ResourceKind {
PROCESS = "arvados#containerRequest",
PROJECT = "arvados#group",
USER = "arvados#user",
+ VIRTUAL_MACHINE = "arvados#virtualMachine",
WORKFLOW = "arvados#workflow",
NONE = "arvados#none"
}
@@ -40,6 +41,7 @@ export enum ResourceObjectType {
GROUP = 'j7d0g',
LOG = '57u5n',
USER = 'tpzed',
+ VIRTUAL_MACHINE = '2x53u',
WORKFLOW = '7fd4e',
}
@@ -73,6 +75,8 @@ export const extractUuidKind = (uuid: string = '') => {
return ResourceKind.LOG;
case ResourceObjectType.WORKFLOW:
return ResourceKind.WORKFLOW;
+ case ResourceObjectType.VIRTUAL_MACHINE:
+ return ResourceKind.VIRTUAL_MACHINE;
default:
return undefined;
}
diff --git a/src/models/virtual-machines.ts b/src/models/virtual-machines.ts
new file mode 100644
index 0000000..050e516
--- /dev/null
+++ b/src/models/virtual-machines.ts
@@ -0,0 +1,18 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Resource } from "~/models/resource";
+
+export interface VirtualMachinesResource extends Resource {
+ hostname: string;
+}
+
+export interface ViruatlMachinesLoginsResource {
+ hostname: string;
+ username: string;
+ public_key: string;
+ user_uuid: string;
+ virtual_machine_uuid: string;
+ authorized_key_uuid: string;
+}
\ No newline at end of file
diff --git a/src/routes/route-change-handlers.ts b/src/routes/route-change-handlers.ts
index ef9e9eb..ca15a15 100644
--- a/src/routes/route-change-handlers.ts
+++ b/src/routes/route-change-handlers.ts
@@ -4,8 +4,8 @@
import { History, Location } from 'history';
import { RootStore } from '~/store/store';
-import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute, matchSharedWithMeRoute, matchRunProcessRoute, matchWorkflowRoute, matchSearchResultsRoute } from './routes';
-import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog } from '~/store/workbench/workbench-actions';
+import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute, matchSharedWithMeRoute, matchRunProcessRoute, matchWorkflowRoute, matchSearchResultsRoute, matchVirtualMachineRoute } from './routes';
+import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog, loadVirtualMachines } from '~/store/workbench/workbench-actions';
import { navigateToRootProject } from '~/store/navigation/navigation-action';
import { loadSharedWithMe, loadRunProcess, loadWorkflow, loadSearchResults } from '~//store/workbench/workbench-actions';
@@ -26,6 +26,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
const searchResultsMatch = matchSearchResultsRoute(pathname);
const sharedWithMeMatch = matchSharedWithMeRoute(pathname);
const runProcessMatch = matchRunProcessRoute(pathname);
+ const virtualMachineMatch = matchVirtualMachineRoute(pathname);
const workflowMatch = matchWorkflowRoute(pathname);
if (projectMatch) {
@@ -50,5 +51,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
store.dispatch(loadWorkflow);
} else if (searchResultsMatch) {
store.dispatch(loadSearchResults);
+ } else if (virtualMachineMatch) {
+ store.dispatch(loadVirtualMachines);
}
};
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
index e5f3493..3723847 100644
--- a/src/routes/routes.ts
+++ b/src/routes/routes.ts
@@ -18,6 +18,7 @@ export const Routes = {
PROCESS_LOGS: `/process-logs/:id(${RESOURCE_UUID_PATTERN})`,
SHARED_WITH_ME: '/shared-with-me',
RUN_PROCESS: '/run-process',
+ VIRTUAL_MACHINES: '/virtual-machines',
WORKFLOWS: '/workflows',
SEARCH_RESULTS: '/search-results'
};
@@ -70,9 +71,12 @@ export const matchSharedWithMeRoute = (route: string) =>
export const matchRunProcessRoute = (route: string) =>
matchPath(route, { path: Routes.RUN_PROCESS });
-
+
export const matchWorkflowRoute = (route: string) =>
matchPath<ResourceRouteParams>(route, { path: Routes.WORKFLOWS });
export const matchSearchResultsRoute = (route: string) =>
matchPath<ResourceRouteParams>(route, { path: Routes.SEARCH_RESULTS });
+
+export const matchVirtualMachineRoute = (route: string) =>
+ matchPath<ResourceRouteParams>(route, { path: Routes.VIRTUAL_MACHINES });
diff --git a/src/services/services.ts b/src/services/services.ts
index 5adf10b..9e9fcc5 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -24,6 +24,7 @@ import { ApiActions } from "~/services/api/api-actions";
import { WorkflowService } from "~/services/workflow-service/workflow-service";
import { SearchService } from '~/services/search-service/search-service';
import { PermissionService } from "~/services/permission-service/permission-service";
+import { VirtualMachinesService } from "~/services/virtual-machines-service/virtual-machines-service";
export type ServiceRepository = ReturnType<typeof createServices>;
@@ -43,6 +44,7 @@ export const createServices = (config: Config, actions: ApiActions) => {
const permissionService = new PermissionService(apiClient, actions);
const projectService = new ProjectService(apiClient, actions);
const userService = new UserService(apiClient, actions);
+ const virtualMachineService = new VirtualMachinesService(apiClient, actions);
const workflowService = new WorkflowService(apiClient, actions);
const ancestorsService = new AncestorService(groupsService, userService);
@@ -71,6 +73,7 @@ export const createServices = (config: Config, actions: ApiActions) => {
searchService,
tagService,
userService,
+ virtualMachineService,
webdavClient,
workflowService,
};
diff --git a/src/services/virtual-machines-service/virtual-machines-service.ts b/src/services/virtual-machines-service/virtual-machines-service.ts
new file mode 100644
index 0000000..c54eff4
--- /dev/null
+++ b/src/services/virtual-machines-service/virtual-machines-service.ts
@@ -0,0 +1,38 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { AxiosInstance } from "axios";
+import { CommonResourceService } from "~/services/common-service/common-resource-service";
+import { VirtualMachinesResource } from '~/models/virtual-machines';
+import { ApiActions } from '~/services/api/api-actions';
+
+export class VirtualMachinesService extends CommonResourceService<VirtualMachinesResource> {
+ constructor(serverApi: AxiosInstance, actions: ApiActions) {
+ super(serverApi, "virtual_machines", actions);
+ }
+
+ getRequestedDate(): string {
+ return localStorage.getItem('requestedDate') || '';
+ }
+
+ saveRequestedDate(date: string) {
+ localStorage.setItem('requestedDate', date);
+ }
+
+ logins(uuid: string) {
+ return CommonResourceService.defaultResponse(
+ this.serverApi
+ .get(`virtual_machines/${uuid}/logins`),
+ this.actions
+ );
+ }
+
+ getAllLogins() {
+ return CommonResourceService.defaultResponse(
+ this.serverApi
+ .get('virtual_machines/get_all_logins'),
+ this.actions
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/store/current-token-dialog/current-token-dialog-actions.tsx b/src/store/current-token-dialog/current-token-dialog-actions.tsx
index 030b18e..fe8186b 100644
--- a/src/store/current-token-dialog/current-token-dialog-actions.tsx
+++ b/src/store/current-token-dialog/current-token-dialog-actions.tsx
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0
import { dialogActions } from "~/store/dialog/dialog-actions";
-import { getProperty } from '../properties/properties';
+import { getProperty } from '~/store/properties/properties';
import { propertiesActions } from '~/store/properties/properties-actions';
import { RootState } from '~/store/store';
diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts
index b63fc2c..c4cf625 100644
--- a/src/store/navigation/navigation-action.ts
+++ b/src/store/navigation/navigation-action.ts
@@ -61,3 +61,5 @@ export const navigateToSharedWithMe = push(Routes.SHARED_WITH_ME);
export const navigateToRunProcess = push(Routes.RUN_PROCESS);
export const navigateToSearchResults = push(Routes.SEARCH_RESULTS);
+
+export const navigateToVirtualMachines = push(Routes.VIRTUAL_MACHINES);
diff --git a/src/store/side-panel-tree/side-panel-tree-actions.ts b/src/store/side-panel-tree/side-panel-tree-actions.ts
index 562f709..0900993 100644
--- a/src/store/side-panel-tree/side-panel-tree-actions.ts
+++ b/src/store/side-panel-tree/side-panel-tree-actions.ts
@@ -20,7 +20,6 @@ export enum SidePanelTreeCategory {
PROJECTS = 'Projects',
SHARED_WITH_ME = 'Shared with me',
WORKFLOWS = 'Workflows',
- RECENT_OPEN = 'Recently open',
FAVORITES = 'Favorites',
TRASH = 'Trash'
}
@@ -44,7 +43,6 @@ export const getSidePanelTreeBranch = (uuid: string) => (treePicker: TreePicker)
const SIDE_PANEL_CATEGORIES = [
SidePanelTreeCategory.WORKFLOWS,
- SidePanelTreeCategory.RECENT_OPEN,
SidePanelTreeCategory.FAVORITES,
SidePanelTreeCategory.TRASH,
];
diff --git a/src/store/store.ts b/src/store/store.ts
index fa2a5be..3f1f4a2 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -43,6 +43,7 @@ import { searchBarReducer } from './search-bar/search-bar-reducer';
import { SEARCH_RESULTS_PANEL_ID } from '~/store/search-results-panel/search-results-panel-actions';
import { SearchResultsMiddlewareService } from './search-results-panel/search-results-middleware-service';
import { resourcesDataReducer } from "~/store/resources-data/resources-data-reducer";
+import { virtualMachinesReducer } from "~/store/virtual-machines/virtual-machines-reducer";
const composeEnhancers =
(process.env.NODE_ENV === 'development' &&
@@ -111,5 +112,6 @@ const createRootReducer = (services: ServiceRepository) => combineReducers({
progressIndicator: progressIndicatorReducer,
runProcessPanel: runProcessPanelReducer,
appInfo: appInfoReducer,
- searchBar: searchBarReducer
+ searchBar: searchBarReducer,
+ virtualMachines: virtualMachinesReducer
});
diff --git a/src/store/virtual-machines/virtual-machines-actions.ts b/src/store/virtual-machines/virtual-machines-actions.ts
new file mode 100644
index 0000000..09072a1
--- /dev/null
+++ b/src/store/virtual-machines/virtual-machines-actions.ts
@@ -0,0 +1,50 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { RootState } from '~/store/store';
+import { ServiceRepository } from "~/services/services";
+import { navigateToVirtualMachines } from "../navigation/navigation-action";
+import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-action';
+import { formatDate } from "~/common/formatters";
+import { unionize, ofType, UnionOf } from "~/common/unionize";
+
+export const virtualMachinesAction = unionize({
+ SET_REQUESTED_DATE: ofType<string>(),
+});
+
+export type VirtualMachineActions = UnionOf<typeof virtualMachinesAction>;
+
+export const VIRTUAL_MACHINES_PANEL = 'virtualMachinesPanel';
+
+export const openVirtualMachines = () =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const virtualMachines = await services.virtualMachineService.list();
+ // const logins = await services.virtualMachineService.logins(virtualMachines.items[0].uuid);
+ // const getAllLogins = await services.virtualMachineService.getAllLogins();
+ console.log(virtualMachines);
+ // console.log(logins);
+ // console.log(getAllLogins);
+ dispatch<any>(navigateToVirtualMachines);
+ };
+
+export const loadRequestedDate = () =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const date = services.virtualMachineService.getRequestedDate();
+ dispatch(virtualMachinesAction.SET_REQUESTED_DATE(date));
+ };
+
+export const saveRequestedDate = () =>
+ (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const date = formatDate((new Date).toISOString());
+ services.virtualMachineService.saveRequestedDate(date);
+ dispatch<any>(loadRequestedDate());
+ };
+
+const virtualMachinesActions = bindDataExplorerActions(VIRTUAL_MACHINES_PANEL);
+
+export const loadVirtualMachinesPanel = () =>
+ (dispatch: Dispatch) => {
+ dispatch(virtualMachinesActions.REQUEST_ITEMS());
+ };
diff --git a/src/store/virtual-machines/virtual-machines-reducer.ts b/src/store/virtual-machines/virtual-machines-reducer.ts
new file mode 100644
index 0000000..26ba2a2
--- /dev/null
+++ b/src/store/virtual-machines/virtual-machines-reducer.ts
@@ -0,0 +1,19 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { virtualMachinesAction, VirtualMachineActions } from '~/store/virtual-machines/virtual-machines-actions';
+
+interface VirtualMachines {
+ date: string;
+}
+
+const initialState: VirtualMachines = {
+ date: ''
+};
+
+export const virtualMachinesReducer = (state = initialState, action: VirtualMachineActions): VirtualMachines =>
+ virtualMachinesAction.match(action, {
+ SET_REQUESTED_DATE: date => ({ ...state, date }),
+ default: () => state
+ });
diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts
index aaf8f26..52d6c9e 100644
--- a/src/store/workbench/workbench-actions.ts
+++ b/src/store/workbench/workbench-actions.ts
@@ -14,7 +14,7 @@ import { favoritePanelActions } from '~/store/favorite-panel/favorite-panel-acti
import { projectPanelColumns } from '~/views/project-panel/project-panel';
import { favoritePanelColumns } from '~/views/favorite-panel/favorite-panel';
import { matchRootRoute } from '~/routes/routes';
-import { setSidePanelBreadcrumbs, setProcessBreadcrumbs, setSharedWithMeBreadcrumbs, setTrashBreadcrumbs } from '../breadcrumbs/breadcrumbs-actions';
+import { setSidePanelBreadcrumbs, setProcessBreadcrumbs, setSharedWithMeBreadcrumbs, setTrashBreadcrumbs, setBreadcrumbs } from '../breadcrumbs/breadcrumbs-actions';
import { navigateToProject } from '../navigation/navigation-action';
import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog';
import { ServiceRepository } from '~/services/services';
@@ -53,6 +53,7 @@ import { collectionPanelActions } from "~/store/collection-panel/collection-pane
import { CollectionResource } from "~/models/collection";
import { searchResultsPanelActions, loadSearchResultsPanel } from '~/store/search-results-panel/search-results-panel-actions';
import { searchResultsPanelColumns } from '~/views/search-results-panel/search-results-panel-view';
+import { loadVirtualMachinesPanel } from '~/store/virtual-machines/virtual-machines-actions';
export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
@@ -390,6 +391,12 @@ export const loadSearchResults = handleFirstTimeLoad(
await dispatch(loadSearchResultsPanel());
});
+export const loadVirtualMachines = handleFirstTimeLoad(
+ async (dispatch: Dispatch<any>) => {
+ await dispatch(loadVirtualMachinesPanel());
+ dispatch(setBreadcrumbs([{ label: 'Virtual Machines' }]));
+ });
+
const finishLoadingProject = (project: GroupContentsResource | string) =>
async (dispatch: Dispatch<any>) => {
const uuid = typeof project === 'string' ? project : project.uuid;
diff --git a/src/views-components/current-token-dialog/current-token-dialog.tsx b/src/views-components/current-token-dialog/current-token-dialog.tsx
index 503206a..934be54 100644
--- a/src/views-components/current-token-dialog/current-token-dialog.tsx
+++ b/src/views-components/current-token-dialog/current-token-dialog.tsx
@@ -3,12 +3,12 @@
// SPDX-License-Identifier: AGPL-3.0
import * as React from 'react';
-import { Dialog, DialogActions, DialogTitle, DialogContent, WithStyles, withStyles, StyleRulesCallback, Button, Typography, Paper } from '@material-ui/core';
+import { Dialog, DialogActions, DialogTitle, DialogContent, WithStyles, withStyles, StyleRulesCallback, Button, Typography } from '@material-ui/core';
import { ArvadosTheme } from '~/common/custom-theme';
import { withDialog } from '~/store/dialog/with-dialog';
import { WithDialogProps } from '~/store/dialog/with-dialog';
import { connect } from 'react-redux';
-import { CurrentTokenDialogData, getCurrentTokenDialogData } from '~/store/current-token-dialog/current-token-dialog-actions';
+import { CurrentTokenDialogData, getCurrentTokenDialogData, CURRENT_TOKEN_DIALOG_NAME } from '~/store/current-token-dialog/current-token-dialog-actions';
import { DefaultCodeSnippet } from '~/components/default-code-snippet/default-code-snippet';
type CssRules = 'link' | 'paper' | 'button';
@@ -36,7 +36,7 @@ type CurrentTokenProps = CurrentTokenDialogData & WithDialogProps<{}> & WithStyl
export const CurrentTokenDialog =
withStyles(styles)(
connect(getCurrentTokenDialogData)(
- withDialog('currentTokenDialog')(
+ withDialog(CURRENT_TOKEN_DIALOG_NAME)(
class extends React.Component<CurrentTokenProps> {
render() {
const { classes, open, closeDialog, ...data } = this.props;
diff --git a/src/views-components/main-app-bar/account-menu.tsx b/src/views-components/main-app-bar/account-menu.tsx
index fdd8123..baf893e 100644
--- a/src/views-components/main-app-bar/account-menu.tsx
+++ b/src/views-components/main-app-bar/account-menu.tsx
@@ -11,6 +11,7 @@ import { DispatchProp, connect } from 'react-redux';
import { logout } from "~/store/auth/auth-action";
import { RootState } from "~/store/store";
import { openCurrentTokenDialog } from '../../store/current-token-dialog/current-token-dialog-actions';
+import { openVirtualMachines } from "~/store/virtual-machines/virtual-machines-actions";
interface AccountMenuProps {
user?: User;
@@ -30,6 +31,7 @@ export const AccountMenu = connect(mapStateToProps)(
<MenuItem>
{getUserFullname(user)}
</MenuItem>
+ <MenuItem onClick={() => dispatch(openVirtualMachines())}>Virtual Machines</MenuItem>
<MenuItem onClick={() => dispatch(openCurrentTokenDialog)}>Current token</MenuItem>
<MenuItem>My account</MenuItem>
<MenuItem onClick={() => dispatch(logout())}>Logout</MenuItem>
diff --git a/src/views-components/main-content-bar/main-content-bar.tsx b/src/views-components/main-content-bar/main-content-bar.tsx
index 6fb419e..b38f85b 100644
--- a/src/views-components/main-content-bar/main-content-bar.tsx
+++ b/src/views-components/main-content-bar/main-content-bar.tsx
@@ -10,6 +10,7 @@ import { detailsPanelActions } from "~/store/details-panel/details-panel-action"
import { connect } from 'react-redux';
import { RootState } from '~/store/store';
import { matchWorkflowRoute } from '~/routes/routes';
+import { matchVirtualMachineRoute } from '~/routes/routes';
interface MainContentBarProps {
onDetailsPanelToggle: () => void;
@@ -22,8 +23,14 @@ const isWorkflowPath = ({ router }: RootState) => {
return !!match;
};
+const isVirtualMachinePath = ({ router }: RootState) => {
+ const pathname = router.location ? router.location.pathname : '';
+ const match = matchVirtualMachineRoute(pathname);
+ return !!match;
+};
+
export const MainContentBar = connect((state: RootState) => ({
- buttonVisible: !isWorkflowPath(state)
+ buttonVisible: !isWorkflowPath(state) && !isVirtualMachinePath(state)
}), {
onDetailsPanelToggle: detailsPanelActions.TOGGLE_DETAILS_PANEL
})((props: MainContentBarProps) =>
@@ -33,11 +40,11 @@ export const MainContentBar = connect((state: RootState) => ({
<Breadcrumbs />
</Grid>
<Grid item>
- {props.buttonVisible ? <Tooltip title="Additional Info">
+ {props.buttonVisible && <Tooltip title="Additional Info">
<IconButton color="inherit" onClick={props.onDetailsPanelToggle}>
<DetailsIcon />
</IconButton>
- </Tooltip> : null}
+ </Tooltip>}
</Grid>
</Grid>
</Toolbar>);
diff --git a/src/views-components/side-panel-tree/side-panel-tree.tsx b/src/views-components/side-panel-tree/side-panel-tree.tsx
index 33ee97f..dd5005c 100644
--- a/src/views-components/side-panel-tree/side-panel-tree.tsx
+++ b/src/views-components/side-panel-tree/side-panel-tree.tsx
@@ -10,7 +10,7 @@ import { TreeItem } from "~/components/tree/tree";
import { ProjectResource } from "~/models/project";
import { ListItemTextIcon } from "~/components/list-item-text-icon/list-item-text-icon";
import { ProjectIcon, FavoriteIcon, ProjectsIcon, ShareMeIcon, TrashIcon } from '~/components/icon/icon';
-import { RecentIcon, WorkflowIcon } from '~/components/icon/icon';
+import { WorkflowIcon } from '~/components/icon/icon';
import { activateSidePanelTreeItem, toggleSidePanelTreeItemCollapse, SIDE_PANEL_TREE, SidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
import { openSidePanelContextMenu } from '~/store/context-menu/context-menu-actions';
import { noop } from 'lodash';
@@ -59,8 +59,6 @@ const getSidePanelIcon = (category: string) => {
return FavoriteIcon;
case SidePanelTreeCategory.PROJECTS:
return ProjectsIcon;
- case SidePanelTreeCategory.RECENT_OPEN:
- return RecentIcon;
case SidePanelTreeCategory.SHARED_WITH_ME:
return ShareMeIcon;
case SidePanelTreeCategory.TRASH:
diff --git a/src/views/virtual-machine-panel/virtual-machine-panel.tsx b/src/views/virtual-machine-panel/virtual-machine-panel.tsx
new file mode 100644
index 0000000..afe019b
--- /dev/null
+++ b/src/views/virtual-machine-panel/virtual-machine-panel.tsx
@@ -0,0 +1,105 @@
+// 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, Typography, Button, Card, CardContent } 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 { Dispatch } from 'redux';
+import { saveRequestedDate, loadRequestedDate } from '~/store/virtual-machines/virtual-machines-actions';
+import { RootState } from '~/store/store';
+
+type CssRules = 'button' | 'codeSnippet' | 'link';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ button: {
+ marginTop: theme.spacing.unit,
+ marginBottom: theme.spacing.unit * 2
+ },
+ codeSnippet: {
+ borderRadius: theme.spacing.unit * 0.5,
+ border: '1px solid',
+ borderColor: theme.palette.grey["400"],
+ maxHeight: '400px'
+ },
+ link: {
+ textDecoration: 'none',
+ color: theme.palette.primary.main
+ },
+});
+
+const mapStateToProps = (state: RootState) => {
+ return {
+ requestedDate: state.virtualMachines.date
+ };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+ saveRequestedDate: () => dispatch<any>(saveRequestedDate()),
+ loadRequestedDate: () => dispatch<any>(loadRequestedDate())
+});
+
+interface VirtualMachinesPanelDataProps {
+ requestedDate: string;
+}
+
+interface VirtualMachinesPanelActionProps {
+ saveRequestedDate: () => void;
+ loadRequestedDate: () => string;
+}
+
+type VirtualMachineProps = VirtualMachinesPanelActionProps & VirtualMachinesPanelDataProps & WithStyles<CssRules>;
+
+export const VirtualMachinePanel = withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(
+ class extends React.Component<VirtualMachineProps> {
+ componentDidMount() {
+ this.props.loadRequestedDate();
+ }
+
+ render() {
+ const { classes, saveRequestedDate, requestedDate } = this.props;
+ return (
+ <Grid container spacing={16}>
+ <Grid item xs={12}>
+ <Card>
+ <CardContent>
+ <Typography variant="body2">
+ You do not have access to any virtual machines. Some Arvados features require using the command line. You may request access to a hosted virtual machine with the command line shell.
+ </Typography>
+ <Button variant="contained" color="primary" className={classes.button} onClick={saveRequestedDate}>
+ SEND REQUEST FOR SHELL ACCESS
+ </Button>
+ {requestedDate &&
+ <Typography variant="body1">
+ A request for shell access was sent on {requestedDate}
+ </Typography>}
+ </CardContent>
+ </Card>
+ </Grid>
+ <Grid item xs={12}>
+ <Card>
+ <CardContent>
+ <Typography variant="body2">
+ In order to access virtual machines using SSH, <Link to='' className={classes.link}>add an SSH key to your account</Link> and add a section like this to your SSH configuration file ( ~/.ssh/config):
+ </Typography>
+ <DefaultCodeSnippet
+ className={classes.codeSnippet}
+ lines={[textSSH]} />
+ </CardContent>
+ </Card>
+ </Grid>
+ </Grid >
+ );
+ }
+ }));
+
+
+
+const textSSH = `Host *.arvados
+ TCPKeepAlive yes
+ ServerAliveInterval 60
+ ProxyCommand ssh -p2222 turnout at switchyard.api.ardev.roche.com -x -a $SSH_PROXY_FLAGS %h`;
\ No newline at end of file
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 744526b..4ebc99b 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -47,6 +47,7 @@ import { SearchResultsPanel } from '~/views/search-results-panel/search-results-
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';
type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
@@ -116,6 +117,7 @@ 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} />
</Switch>
</Grid>
</Grid>
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list