From 4d73bcbaa792da5854f4d266dd7be32fc99e3289 Mon Sep 17 00:00:00 2001 From: Pawel Kowalczyk Date: Fri, 16 Nov 2018 13:00:21 +0100 Subject: [PATCH] virtual-machines-panel-init Feature #13864 Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk --- src/components/icon/icon.tsx | 2 - src/models/resource.ts | 4 + src/models/virtual-machines.ts | 18 +++ src/routes/route-change-handlers.ts | 7 +- src/routes/routes.ts | 6 +- src/services/services.ts | 3 + .../virtual-machines-service.ts | 38 +++++++ .../current-token-dialog-actions.tsx | 2 +- src/store/navigation/navigation-action.ts | 2 + .../side-panel-tree-actions.ts | 2 - src/store/store.ts | 4 +- .../virtual-machines-actions.ts | 50 +++++++++ .../virtual-machines-reducer.ts | 19 ++++ src/store/workbench/workbench-actions.ts | 9 +- .../current-token-dialog.tsx | 6 +- .../main-app-bar/account-menu.tsx | 2 + .../main-content-bar/main-content-bar.tsx | 13 ++- .../side-panel-tree/side-panel-tree.tsx | 4 +- .../virtual-machine-panel.tsx | 105 ++++++++++++++++++ src/views/workbench/workbench.tsx | 2 + 20 files changed, 279 insertions(+), 19 deletions(-) create mode 100644 src/models/virtual-machines.ts create mode 100644 src/services/virtual-machines-service/virtual-machines-service.ts create mode 100644 src/store/virtual-machines/virtual-machines-actions.ts create mode 100644 src/store/virtual-machines/virtual-machines-reducer.ts create mode 100644 src/views/virtual-machine-panel/virtual-machine-panel.tsx diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx index a0fbd6ef..4e1a0635 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) => ; export const ProjectIcon: IconType = (props) => ; export const ProjectsIcon: IconType = (props) => ; export const ProvenanceGraphIcon: IconType = (props) => ; -export const RecentIcon: IconType = (props) => ; export const RemoveIcon: IconType = (props) => ; export const RemoveFavoriteIcon: IconType = (props) => ; export const RenameIcon: IconType = (props) => ; diff --git a/src/models/resource.ts b/src/models/resource.ts index b8156cf2..f200713e 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 00000000..050e516f --- /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 ef9e9ebc..ca15a150 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 e5f34935..3723847c 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(route, { path: Routes.WORKFLOWS }); export const matchSearchResultsRoute = (route: string) => matchPath(route, { path: Routes.SEARCH_RESULTS }); + +export const matchVirtualMachineRoute = (route: string) => + matchPath(route, { path: Routes.VIRTUAL_MACHINES }); diff --git a/src/services/services.ts b/src/services/services.ts index 5adf10b3..9e9fcc59 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; @@ -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 00000000..c54eff47 --- /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 { + 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 030b18e2..fe8186b7 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 b63fc2cb..c4cf6252 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 562f7096..09009930 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 fa2a5be9..3f1f4a25 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 00000000..09072a1b --- /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(), +}); + +export type VirtualMachineActions = UnionOf; + +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(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(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 00000000..26ba2a22 --- /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 aaf8f266..52d6c9e6 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) => { + await dispatch(loadVirtualMachinesPanel()); + dispatch(setBreadcrumbs([{ label: 'Virtual Machines' }])); + }); + const finishLoadingProject = (project: GroupContentsResource | string) => async (dispatch: Dispatch) => { 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 503206a6..934be54d 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 { 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 fdd8123f..baf893e2 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)( {getUserFullname(user)} + dispatch(openVirtualMachines())}>Virtual Machines dispatch(openCurrentTokenDialog)}>Current token My account dispatch(logout())}>Logout 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 6fb419e3..b38f85b5 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) => ({ - {props.buttonVisible ? + {props.buttonVisible && - : null} + } ); 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 33ee97f9..dd5005c3 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 00000000..afe019b1 --- /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 = (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(saveRequestedDate()), + loadRequestedDate: () => dispatch(loadRequestedDate()) +}); + +interface VirtualMachinesPanelDataProps { + requestedDate: string; +} + +interface VirtualMachinesPanelActionProps { + saveRequestedDate: () => void; + loadRequestedDate: () => string; +} + +type VirtualMachineProps = VirtualMachinesPanelActionProps & VirtualMachinesPanelDataProps & WithStyles; + +export const VirtualMachinePanel = withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)( + class extends React.Component { + componentDidMount() { + this.props.loadRequestedDate(); + } + + render() { + const { classes, saveRequestedDate, requestedDate } = this.props; + return ( + + + + + + 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. + + + {requestedDate && + + A request for shell access was sent on {requestedDate} + } + + + + + + + + In order to access virtual machines using SSH, add an SSH key to your account and add a section like this to your SSH configuration file ( ~/.ssh/config): + + + + + + + ); + } + })); + + + +const textSSH = `Host *.arvados + TCPKeepAlive yes + ServerAliveInterval 60 + ProxyCommand ssh -p2222 turnout@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 744526b1..4ebc99bd 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 = + -- 2.30.2