[ARVADOS-WORKBENCH2] created: 1.2.0-914-ga0aedfc

Git user git at public.curoverse.com
Mon Nov 19 10:45:26 EST 2018


        at  a0aedfcf68e7b3d4caeee3e36d776ca0b34780bf (commit)


commit a0aedfcf68e7b3d4caeee3e36d776ca0b34780bf
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date:   Mon Nov 19 16:45:05 2018 +0100

    repositories-panel-init
    
    Feature #13865
    
    Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>

diff --git a/src/components/data-explorer/data-explorer.tsx b/src/components/data-explorer/data-explorer.tsx
index 08f52d0..f863ba1 100644
--- a/src/components/data-explorer/data-explorer.tsx
+++ b/src/components/data-explorer/data-explorer.tsx
@@ -4,13 +4,13 @@
 
 import * as React from 'react';
 import { Grid, Paper, Toolbar, StyleRulesCallback, withStyles, WithStyles, TablePagination, IconButton, Tooltip } from '@material-ui/core';
-import MoreVertIcon from "@material-ui/icons/MoreVert";
 import { ColumnSelector } from "../column-selector/column-selector";
 import { DataTable, DataColumns } from "../data-table/data-table";
 import { DataColumn, SortDirection } from "../data-table/data-column";
 import { DataTableFilterItem } from '../data-table-filters/data-table-filters';
 import { SearchInput } from '../search-input/search-input';
 import { ArvadosTheme } from "~/common/custom-theme";
+import { MoreOptionsIcon } from '~/components/icon/icon';
 
 type CssRules = 'searchBox' | "toolbar" | "footer" | "root" | 'moreOptionsButton';
 
@@ -127,7 +127,7 @@ export const DataExplorer = withStyles(styles)(
             <Grid container justify="center">
                 <Tooltip title="More options" disableFocusListener>
                     <IconButton className={this.props.classes.moreOptionsButton} onClick={event => this.props.onContextMenu(event, item)}>
-                        <MoreVertIcon />
+                        <MoreOptionsIcon />
                     </IconButton>
                 </Tooltip>
             </Grid>
diff --git a/src/index.tsx b/src/index.tsx
index efe3a57..922720a 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -48,6 +48,7 @@ import { getBuildInfo } from '~/common/app-info';
 import { DragDropContextProvider } from 'react-dnd';
 import HTML5Backend from 'react-dnd-html5-backend';
 import { initAdvanceFormProjectsTree } from '~/store/search-bar/search-bar-actions';
+import { repositoryActionSet } from '~/views-components/context-menu/action-sets/repository-action-set';
 
 console.log(`Starting arvados [${getBuildInfo()}]`);
 
@@ -64,6 +65,7 @@ addMenuActionSet(ContextMenuKind.TRASHED_COLLECTION, trashedCollectionActionSet)
 addMenuActionSet(ContextMenuKind.PROCESS, processActionSet);
 addMenuActionSet(ContextMenuKind.PROCESS_RESOURCE, processResourceActionSet);
 addMenuActionSet(ContextMenuKind.TRASH, trashActionSet);
+addMenuActionSet(ContextMenuKind.REPOSITORY, repositoryActionSet);
 
 fetchConfig()
     .then(({ config, apiHost }) => {
diff --git a/src/models/repositories.ts b/src/models/repositories.ts
new file mode 100644
index 0000000..63494bc
--- /dev/null
+++ b/src/models/repositories.ts
@@ -0,0 +1,10 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Resource } from "~/models/resource";
+
+export interface RepositoriesResource extends Resource {
+    name: string;
+    cloneUrls: string[];
+}
diff --git a/src/models/resource.ts b/src/models/resource.ts
index b8156cf..520520f 100644
--- a/src/models/resource.ts
+++ b/src/models/resource.ts
@@ -28,6 +28,7 @@ export enum ResourceKind {
     LOG = "arvados#log",
     PROCESS = "arvados#containerRequest",
     PROJECT = "arvados#group",
+    REPOSITORY = "arvados#repository",
     USER = "arvados#user",
     WORKFLOW = "arvados#workflow",
     NONE = "arvados#none"
@@ -39,6 +40,7 @@ export enum ResourceObjectType {
     CONTAINER_REQUEST = 'xvhdp',
     GROUP = 'j7d0g',
     LOG = '57u5n',
+    REPOSITORY = 's0uqq',
     USER = 'tpzed',
     WORKFLOW = '7fd4e',
 }
@@ -73,6 +75,8 @@ export const extractUuidKind = (uuid: string = '') => {
             return ResourceKind.LOG;
         case ResourceObjectType.WORKFLOW:
             return ResourceKind.WORKFLOW;
+        case ResourceObjectType.REPOSITORY:
+            return ResourceKind.REPOSITORY;
         default:
             return undefined;
     }
diff --git a/src/routes/route-change-handlers.ts b/src/routes/route-change-handlers.ts
index ef9e9eb..a4a53d7 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, matchRepositoriesRoute } from './routes';
+import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog, loadRepositories } from '~/store/workbench/workbench-actions';
 import { navigateToRootProject } from '~/store/navigation/navigation-action';
 import { loadSharedWithMe, loadRunProcess, loadWorkflow, loadSearchResults } from '~//store/workbench/workbench-actions';
 
@@ -23,6 +23,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
     const trashMatch = matchTrashRoute(pathname);
     const processMatch = matchProcessRoute(pathname);
     const processLogMatch = matchProcessLogRoute(pathname);
+    const repositoryMatch = matchRepositoriesRoute(pathname); 
     const searchResultsMatch = matchSearchResultsRoute(pathname);
     const sharedWithMeMatch = matchSharedWithMeRoute(pathname);
     const runProcessMatch = matchRunProcessRoute(pathname);
@@ -50,5 +51,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
         store.dispatch(loadWorkflow);
     } else if (searchResultsMatch) {
         store.dispatch(loadSearchResults);
+    } else if(repositoryMatch) {
+        store.dispatch(loadRepositories);
     }
 };
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
index e5f3493..5dbecb4 100644
--- a/src/routes/routes.ts
+++ b/src/routes/routes.ts
@@ -16,6 +16,7 @@ export const Routes = {
     FAVORITES: '/favorites',
     TRASH: '/trash',
     PROCESS_LOGS: `/process-logs/:id(${RESOURCE_UUID_PATTERN})`,
+    REPOSITORIES: '/repositories',
     SHARED_WITH_ME: '/shared-with-me',
     RUN_PROCESS: '/run-process',
     WORKFLOWS: '/workflows',
@@ -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 matchRepositoriesRoute = (route: string) =>
+    matchPath<ResourceRouteParams>(route, { path: Routes.REPOSITORIES });
\ No newline at end of file
diff --git a/src/services/repositories-service/repositories-service.ts b/src/services/repositories-service/repositories-service.ts
new file mode 100644
index 0000000..4bc6602
--- /dev/null
+++ b/src/services/repositories-service/repositories-service.ts
@@ -0,0 +1,22 @@
+// 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 { RepositoriesResource } from '~/models/repositories';
+import { ApiActions } from '~/services/api/api-actions';
+
+ export class RepositoriesService extends CommonResourceService<RepositoriesResource> {
+    constructor(serverApi: AxiosInstance, actions: ApiActions) {
+        super(serverApi, "repositories", actions);
+    }
+
+     getAllPermissions() {
+        return CommonResourceService.defaultResponse(
+            this.serverApi
+                .get('repositories/get_all_permissions'),
+            this.actions
+        );
+    }
+} 
\ No newline at end of file
diff --git a/src/services/services.ts b/src/services/services.ts
index 5adf10b..2bc955f 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 { RepositoriesService } from '~/services/repositories-service/repositories-service';
 
 export type ServiceRepository = ReturnType<typeof createServices>;
 
@@ -42,6 +43,7 @@ export const createServices = (config: Config, actions: ApiActions) => {
     const logService = new LogService(apiClient, actions);
     const permissionService = new PermissionService(apiClient, actions);
     const projectService = new ProjectService(apiClient, actions);
+    const repositoriesService = new RepositoriesService(apiClient, actions);
     const userService = new UserService(apiClient, actions);
     const workflowService = new WorkflowService(apiClient, actions);
 
@@ -68,6 +70,7 @@ export const createServices = (config: Config, actions: ApiActions) => {
         logService,
         permissionService,
         projectService,
+        repositoriesService,
         searchService,
         tagService,
         userService,
diff --git a/src/store/context-menu/context-menu-actions.ts b/src/store/context-menu/context-menu-actions.ts
index 32bc47b..a59f376 100644
--- a/src/store/context-menu/context-menu-actions.ts
+++ b/src/store/context-menu/context-menu-actions.ts
@@ -12,7 +12,6 @@ import { ProjectResource } from '~/models/project';
 import { UserResource } from '~/models/user';
 import { isSidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
 import { extractUuidKind, ResourceKind } from '~/models/resource';
-import { matchProcessRoute } from '~/routes/routes';
 import { Process } from '~/store/processes/process';
 
 export const contextMenuActions = unionize({
@@ -60,6 +59,17 @@ export const openCollectionFilesContextMenu = (event: React.MouseEvent<HTMLEleme
         }));
     };
 
+export const openRepositoryContextMenu = (event: React.MouseEvent<HTMLElement>) =>
+    (dispatch: Dispatch, getState: () => RootState) => {
+        dispatch<any>(openContextMenu(event, {
+            name: '',
+            uuid: '',
+            ownerUuid: '',
+            kind: ResourceKind.REPOSITORY,
+            menuKind: ContextMenuKind.REPOSITORY
+        }));
+    };
+
 export const openRootProjectContextMenu = (event: React.MouseEvent<HTMLElement>, projectUuid: string) =>
     (dispatch: Dispatch, getState: () => RootState) => {
         const res = getResource<UserResource>(projectUuid)(getState().resources);
diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts
index b63fc2c..ce59969 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 navigateToRepositories = push(Routes.REPOSITORIES);
diff --git a/src/store/repositories/repositories-actions.ts b/src/store/repositories/repositories-actions.ts
new file mode 100644
index 0000000..7f953b8
--- /dev/null
+++ b/src/store/repositories/repositories-actions.ts
@@ -0,0 +1,36 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-action';
+import { RootState } from '~/store/store';
+import { ServiceRepository } from "~/services/services";
+import { navigateToRepositories } from "~/store/navigation/navigation-action";
+import { unionize, ofType, UnionOf } from "~/common/unionize";
+
+export const repositoriesActions = unionize({
+    SET_REPOSITORIES: ofType<any>(),
+});
+
+ export type RepositoriesActions = UnionOf<typeof repositoriesActions>;
+
+export const REPOSITORIES_PANEL = 'repositoriesPanel';
+
+const repositoriesBindedActions = bindDataExplorerActions(REPOSITORIES_PANEL);
+
+export const openRepositoriesPanel = () =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch<any>(navigateToRepositories);
+    };
+
+export const loadRepositoriesData = () =>
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        const repositories = await services.repositoriesService.list();
+        dispatch(repositoriesActions.SET_REPOSITORIES(repositories.items));
+    };
+
+export const loadRepositoriesPanel = () =>
+    (dispatch: Dispatch) => {
+        dispatch(repositoriesBindedActions.REQUEST_ITEMS());
+    };
\ No newline at end of file
diff --git a/src/store/repositories/repositories-reducer.ts b/src/store/repositories/repositories-reducer.ts
new file mode 100644
index 0000000..f277c31
--- /dev/null
+++ b/src/store/repositories/repositories-reducer.ts
@@ -0,0 +1,20 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { repositoriesActions, RepositoriesActions } from '~/store/repositories/repositories-actions';
+import { RepositoriesResource } from '~/models/repositories';
+
+interface Repositories {
+    items: RepositoriesResource[];
+}
+
+const initialState: Repositories = {
+    items: []
+};
+
+export const repositoriesReducer = (state = initialState, action: RepositoriesActions): Repositories =>
+    repositoriesActions.match(action, {
+        SET_REPOSITORIES: items => ({ ...state, items }),
+        default: () => state
+    });
\ No newline at end of file
diff --git a/src/store/store.ts b/src/store/store.ts
index fa2a5be..5e648c9 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 { repositoriesReducer } from '~/store/repositories/repositories-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,
+    repositories: repositoriesReducer
 });
diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts
index aaf8f26..c6440fd 100644
--- a/src/store/workbench/workbench-actions.ts
+++ b/src/store/workbench/workbench-actions.ts
@@ -3,23 +3,23 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { Dispatch } from 'redux';
-import { RootState } from "../store";
+import { RootState } from "~/store/store";
 import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
-import { snackbarActions } from '../snackbar/snackbar-actions';
-import { loadFavoritePanel } from '../favorite-panel/favorite-panel-action';
+import { snackbarActions } from '~/store/snackbar/snackbar-actions';
+import { loadFavoritePanel } from '~/store/favorite-panel/favorite-panel-action';
 import { openProjectPanel, projectPanelActions, setIsProjectPanelTrashed } from '~/store/project-panel/project-panel-action';
-import { activateSidePanelTreeItem, initSidePanelTree, SidePanelTreeCategory, loadSidePanelTreeProjects } from '../side-panel-tree/side-panel-tree-actions';
-import { loadResource, updateResources } from '../resources/resources-actions';
+import { activateSidePanelTreeItem, initSidePanelTree, SidePanelTreeCategory, loadSidePanelTreeProjects } from '~/store/side-panel-tree/side-panel-tree-actions';
+import { loadResource, updateResources } from '~/store/resources/resources-actions';
 import { favoritePanelActions } from '~/store/favorite-panel/favorite-panel-action';
 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 { navigateToProject } from '../navigation/navigation-action';
+import { setSidePanelBreadcrumbs, setProcessBreadcrumbs, setSharedWithMeBreadcrumbs, setTrashBreadcrumbs, setBreadcrumbs } from '~/store/breadcrumbs/breadcrumbs-actions';
+import { navigateToProject } from '~/store/navigation/navigation-action';
 import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog';
 import { ServiceRepository } from '~/services/services';
-import { getResource } from '../resources/resources';
-import { getProjectPanelCurrentUuid } from '../project-panel/project-panel-action';
+import { getResource } from '~/store/resources/resources';
+import { getProjectPanelCurrentUuid } from '~/store/project-panel/project-panel-action';
 import * as projectCreateActions from '~/store/projects/project-create-actions';
 import * as projectMoveActions from '~/store/projects/project-move-actions';
 import * as projectUpdateActions from '~/store/projects/project-update-actions';
@@ -27,21 +27,21 @@ import * as collectionCreateActions from '~/store/collections/collection-create-
 import * as collectionCopyActions from '~/store/collections/collection-copy-actions';
 import * as collectionUpdateActions from '~/store/collections/collection-update-actions';
 import * as collectionMoveActions from '~/store/collections/collection-move-actions';
-import * as processesActions from '../processes/processes-actions';
+import * as processesActions from '~/store/processes/processes-actions';
 import * as processMoveActions from '~/store/processes/process-move-actions';
 import * as processUpdateActions from '~/store/processes/process-update-actions';
 import * as processCopyActions from '~/store/processes/process-copy-actions';
 import { trashPanelColumns } from "~/views/trash-panel/trash-panel";
 import { loadTrashPanel, trashPanelActions } from "~/store/trash-panel/trash-panel-action";
-import { initProcessLogsPanel } from '../process-logs-panel/process-logs-panel-actions';
+import { initProcessLogsPanel } from '~/store/process-logs-panel/process-logs-panel-actions';
 import { loadProcessPanel } from '~/store/process-panel/process-panel-actions';
 import { sharedWithMePanelActions } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
-import { loadSharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel-actions';
+import { loadSharedWithMePanel } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
 import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog';
 import { loadWorkflowPanel, workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions';
 import { workflowPanelColumns } from '~/views/workflow-panel/workflow-panel-view';
 import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
-import { getProgressIndicator } from '../progress-indicator/progress-indicator-reducer';
+import { getProgressIndicator } from '~/store/progress-indicator/progress-indicator-reducer';
 import { ResourceKind, extractUuidKind } from '~/models/resource';
 import { FilterBuilder } from '~/services/api/filter-builder';
 import { GroupContentsResource } from '~/services/groups-service/groups-service';
@@ -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 { loadRepositoriesPanel } from '~/store/repositories/repositories-actions';
 
 export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
 
@@ -390,6 +391,12 @@ export const loadSearchResults = handleFirstTimeLoad(
         await dispatch(loadSearchResultsPanel());
     });
 
+export const loadRepositories = handleFirstTimeLoad(
+    async (dispatch: Dispatch<any>) => {
+        await dispatch(loadRepositoriesPanel());
+        dispatch(setBreadcrumbs([{ label: 'Repositories' }]));
+    });
+
 const finishLoadingProject = (project: GroupContentsResource | string) =>
     async (dispatch: Dispatch<any>) => {
         const uuid = typeof project === 'string' ? project : project.uuid;
diff --git a/src/views-components/context-menu/action-sets/repository-action-set.ts b/src/views-components/context-menu/action-sets/repository-action-set.ts
new file mode 100644
index 0000000..2f3a985
--- /dev/null
+++ b/src/views-components/context-menu/action-sets/repository-action-set.ts
@@ -0,0 +1,34 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ContextMenuActionSet } from "~/views-components/context-menu/context-menu-action-set";
+import { AdvancedIcon, RemoveIcon, ShareIcon } from "~/components/icon/icon";
+import { openFileRemoveDialog, openRenameFileDialog } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
+
+export const repositoryActionSet: ContextMenuActionSet = [[{
+    name: "Attributes",
+    icon: AdvancedIcon,
+    execute: (dispatch, resource) => {
+        dispatch<any>(openRenameFileDialog({ name: resource.name, id: resource.uuid }));
+    }
+}, {
+    name: "Share",
+    icon: ShareIcon,
+    execute: (dispatch, resource) => {
+        dispatch<any>(openRenameFileDialog({ name: resource.name, id: resource.uuid }));
+    }
+}, {
+    name: "Advanced",
+    icon: AdvancedIcon,
+    execute: (dispatch, resource) => {
+        dispatch<any>(openFileRemoveDialog(resource.uuid));
+    }
+},
+{
+    name: "Remove",
+    icon: RemoveIcon,
+    execute: (dispatch, resource) => {
+        dispatch<any>(openFileRemoveDialog(resource.uuid));
+    }
+}]];
diff --git a/src/views-components/context-menu/context-menu.tsx b/src/views-components/context-menu/context-menu.tsx
index b6d2b91..30ecc98 100644
--- a/src/views-components/context-menu/context-menu.tsx
+++ b/src/views-components/context-menu/context-menu.tsx
@@ -68,5 +68,6 @@ export enum ContextMenuKind {
     TRASHED_COLLECTION = 'TrashedCollection',
     PROCESS = "Process",
     PROCESS_RESOURCE = 'ProcessResource',
-    PROCESS_LOGS = "ProcessLogs"
+    PROCESS_LOGS = "ProcessLogs",
+    REPOSITORY = "Repository"
 }
diff --git a/src/views-components/main-app-bar/account-menu.tsx b/src/views-components/main-app-bar/account-menu.tsx
index fdd8123..c643fef 100644
--- a/src/views-components/main-app-bar/account-menu.tsx
+++ b/src/views-components/main-app-bar/account-menu.tsx
@@ -10,7 +10,8 @@ import { UserPanelIcon } from "~/components/icon/icon";
 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 { openCurrentTokenDialog } from '~/store/current-token-dialog/current-token-dialog-actions';
+import { openRepositoriesPanel } from "~/store/repositories/repositories-actions";
 
 interface AccountMenuProps {
     user?: User;
@@ -30,6 +31,7 @@ export const AccountMenu = connect(mapStateToProps)(
                 <MenuItem>
                     {getUserFullname(user)}
                 </MenuItem>
+                <MenuItem onClick={() => dispatch(openRepositoriesPanel())}>Repositories</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 b047837..2039f6b 100644
--- a/src/views-components/main-content-bar/main-content-bar.tsx
+++ b/src/views-components/main-content-bar/main-content-bar.tsx
@@ -6,10 +6,9 @@ import * as React from "react";
 import { Toolbar, IconButton, Tooltip, Grid } from "@material-ui/core";
 import { DetailsIcon } from "~/components/icon/icon";
 import { Breadcrumbs } from "~/views-components/breadcrumbs/breadcrumbs";
-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 { matchWorkflowRoute, matchRepositoriesRoute } from '~/routes/routes';
 import { toggleDetailsPanel } from '~/store/details-panel/details-panel-action';
 
 interface MainContentBarProps {
@@ -23,8 +22,14 @@ const isWorkflowPath = ({ router }: RootState) => {
     return !!match;
 };
 
+const isRepositoriesPath = ({ router }: RootState) => {
+    const pathname = router.location ? router.location.pathname : '';
+    const match = matchRepositoriesRoute(pathname);
+    return !!match;
+};
+
 export const MainContentBar = connect((state: RootState) => ({
-    buttonVisible: !isWorkflowPath(state)
+    buttonVisible: !isWorkflowPath(state) && !isRepositoriesPath(state)
 }), {
         onDetailsPanelToggle: toggleDetailsPanel
     })((props: MainContentBarProps) =>
@@ -34,11 +39,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/repositories-panel/repositories-panel.tsx b/src/views/repositories-panel/repositories-panel.tsx
new file mode 100644
index 0000000..3d2d32c
--- /dev/null
+++ b/src/views/repositories-panel/repositories-panel.tsx
@@ -0,0 +1,150 @@
+// 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, 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 { Link } from 'react-router-dom';
+import { Dispatch, compose } from 'redux';
+import { RootState } from '~/store/store';
+import { HelpIcon, AddIcon, MoreOptionsIcon } from '~/components/icon/icon';
+import { loadRepositoriesData } from '~/store/repositories/repositories-actions';
+import { RepositoriesResource } from '~/models/repositories';
+import { openRepositoryContextMenu } from '~/store/context-menu/context-menu-actions';
+
+
+type CssRules = 'link' | 'button' | 'icon' | 'iconRow' | 'moreOptionsButton' | 'moreOptions' | 'cloneUrls';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    link: {
+        textDecoration: 'none',
+        color: theme.palette.primary.main,
+        "&:hover": {
+            color: theme.palette.primary.dark,
+            transition: 'all 0.5s ease'
+        }
+    },
+    button: {
+        textAlign: 'right',
+        alignSelf: 'center'
+    },
+    icon: {
+        cursor: 'pointer',
+        color: theme.palette.grey["500"],
+        "&:hover": {
+            color: theme.palette.common.black,
+            transition: 'all 0.5s ease'
+        }
+    },
+    iconRow: {
+        paddingTop: theme.spacing.unit * 2,
+        textAlign: 'right'
+    },
+    moreOptionsButton: {
+        padding: 0
+    },
+    moreOptions: {
+        textAlign: 'right',
+        '&:last-child': {
+            paddingRight: 0
+        }
+    },
+    cloneUrls: {
+        whiteSpace: 'pre-wrap'
+    }
+});
+
+const mapStateToProps = (state: RootState) => {
+    return {
+        repositories: state.repositories.items
+    };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch): Pick<RepositoriesActionProps, 'onOptionsMenuOpen' | 'loadRepositories'> => ({
+    loadRepositories: () => dispatch<any>(loadRepositoriesData()),
+    onOptionsMenuOpen: (event) => {
+        dispatch<any>(openRepositoryContextMenu(event));
+    },
+});
+
+interface RepositoriesActionProps {
+    loadRepositories: () => void;
+    onOptionsMenuOpen: (event: React.MouseEvent<HTMLElement>) => void;
+}
+
+interface RepositoriesDataProps {
+    repositories: RepositoriesResource[];
+}
+
+
+type RepositoriesProps = RepositoriesDataProps & RepositoriesActionProps & WithStyles<CssRules>;
+
+export const RepositoriesPanel = compose(
+    withStyles(styles),
+    connect(mapStateToProps, mapDispatchToProps))(
+        class extends React.Component<RepositoriesProps> {
+            componentDidMount() {
+                this.props.loadRepositories();
+            }
+            render() {
+                const { classes, repositories, onOptionsMenuOpen } = this.props;
+                console.log(repositories);
+                return (
+                    <Card>
+                        <CardContent>
+                            <Grid container direction="row">
+                                <Grid item xs={8}>
+                                    <Typography variant="body2">
+                                        When you are using an Arvados virtual machine, you should clone the https:// URLs. This will authenticate automatically using your API token. <br />
+                                        In order to clone git repositories using SSH, <Link to='' className={classes.link}>add an SSH key to your account</Link> and clone the git@ URLs.
+                                    </Typography>
+                                </Grid>
+                                <Grid item xs={4} className={classes.button}>
+                                    <Button variant="contained" color="primary">
+                                        <AddIcon /> NEW REPOSITORY
+                                    </Button>
+                                </Grid>
+                            </Grid>
+                            <Grid item xs={12}>
+                                <div className={classes.iconRow}>
+                                    <Tooltip title="Sample git quick start">
+                                        <IconButton className={classes.moreOptionsButton}>
+                                            <HelpIcon className={classes.icon} />
+                                        </IconButton>
+                                    </Tooltip>
+                                </div>
+                            </Grid>
+                            <Grid item xs={12}>
+                                {repositories && <Table>
+                                    <TableHead>
+                                        <TableRow>
+                                            <TableCell>Name</TableCell>
+                                            <TableCell>URL</TableCell>
+                                            <TableCell />
+                                        </TableRow>
+                                    </TableHead>
+                                    <TableBody>
+                                        {repositories.map((repository, index) =>
+                                            <TableRow key={index}>
+                                                <TableCell>{repository.name}</TableCell>
+                                                <TableCell className={classes.cloneUrls}>{repository.cloneUrls.join("\n")}</TableCell>
+                                                <TableCell className={classes.moreOptions}>
+                                                    <Tooltip title="More options" disableFocusListener>
+                                                        <IconButton onClick={onOptionsMenuOpen} className={classes.moreOptionsButton}>
+                                                            <MoreOptionsIcon />
+                                                        </IconButton>
+                                                    </Tooltip>
+                                                </TableCell>
+                                            </TableRow>)}
+                                    </TableBody>
+                                </Table>}
+                            </Grid>
+                        </CardContent>
+                    </Card>
+                );
+            }
+        }
+    );
\ No newline at end of file
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 8d1fb67..d0509d0 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -48,6 +48,7 @@ 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 { ProjectPropertiesDialog } from '~/views-components/project-properties-dialog/project-properties-dialog';
+import { RepositoriesPanel } from '~/views/repositories-panel/repositories-panel';
 
 type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
 
@@ -117,6 +118,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.REPOSITORIES} component={RepositoriesPanel} />
                             </Switch>
                         </Grid>
                     </Grid>

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list