From a0aedfcf68e7b3d4caeee3e36d776ca0b34780bf Mon Sep 17 00:00:00 2001 From: Pawel Kowalczyk Date: Mon, 19 Nov 2018 16:45:05 +0100 Subject: [PATCH] repositories-panel-init Feature #13865 Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk --- .../data-explorer/data-explorer.tsx | 4 +- src/index.tsx | 2 + src/models/repositories.ts | 10 ++ src/models/resource.ts | 4 + src/routes/route-change-handlers.ts | 7 +- src/routes/routes.ts | 6 +- .../repositories-service.ts | 22 +++ src/services/services.ts | 3 + .../context-menu/context-menu-actions.ts | 12 +- src/store/navigation/navigation-action.ts | 2 + .../repositories/repositories-actions.ts | 36 +++++ .../repositories/repositories-reducer.ts | 20 +++ src/store/store.ts | 4 +- src/store/workbench/workbench-actions.ts | 33 ++-- .../action-sets/repository-action-set.ts | 34 ++++ .../context-menu/context-menu.tsx | 3 +- .../main-app-bar/account-menu.tsx | 4 +- .../main-content-bar/main-content-bar.tsx | 15 +- .../repositories-panel/repositories-panel.tsx | 150 ++++++++++++++++++ src/views/workbench/workbench.tsx | 2 + 20 files changed, 346 insertions(+), 27 deletions(-) create mode 100644 src/models/repositories.ts create mode 100644 src/services/repositories-service/repositories-service.ts create mode 100644 src/store/repositories/repositories-actions.ts create mode 100644 src/store/repositories/repositories-reducer.ts create mode 100644 src/views-components/context-menu/action-sets/repository-action-set.ts create mode 100644 src/views/repositories-panel/repositories-panel.tsx diff --git a/src/components/data-explorer/data-explorer.tsx b/src/components/data-explorer/data-explorer.tsx index 08f52d00..f863ba13 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)( this.props.onContextMenu(event, item)}> - + diff --git a/src/index.tsx b/src/index.tsx index efe3a576..922720a4 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 00000000..63494bc6 --- /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 b8156cf2..520520f7 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 ef9e9ebc..a4a53d70 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 e5f34935..5dbecb45 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(route, { path: Routes.WORKFLOWS }); export const matchSearchResultsRoute = (route: string) => matchPath(route, { path: Routes.SEARCH_RESULTS }); + +export const matchRepositoriesRoute = (route: string) => + matchPath(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 00000000..4bc66020 --- /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 { + 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 5adf10b3..2bc955f2 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; @@ -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 32bc47b6..a59f376d 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) => + (dispatch: Dispatch, getState: () => RootState) => { + dispatch(openContextMenu(event, { + name: '', + uuid: '', + ownerUuid: '', + kind: ResourceKind.REPOSITORY, + menuKind: ContextMenuKind.REPOSITORY + })); + }; + export const openRootProjectContextMenu = (event: React.MouseEvent, projectUuid: string) => (dispatch: Dispatch, getState: () => RootState) => { const res = getResource(projectUuid)(getState().resources); diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts index b63fc2cb..ce599699 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 00000000..7f953b82 --- /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(), +}); + + export type RepositoriesActions = UnionOf; + +export const REPOSITORIES_PANEL = 'repositoriesPanel'; + +const repositoriesBindedActions = bindDataExplorerActions(REPOSITORIES_PANEL); + +export const openRepositoriesPanel = () => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + dispatch(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 00000000..f277c314 --- /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 fa2a5be9..5e648c99 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 aaf8f266..c6440fd9 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) => { + await dispatch(loadRepositoriesPanel()); + dispatch(setBreadcrumbs([{ label: 'Repositories' }])); + }); + const finishLoadingProject = (project: GroupContentsResource | string) => async (dispatch: Dispatch) => { 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 00000000..2f3a985f --- /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(openRenameFileDialog({ name: resource.name, id: resource.uuid })); + } +}, { + name: "Share", + icon: ShareIcon, + execute: (dispatch, resource) => { + dispatch(openRenameFileDialog({ name: resource.name, id: resource.uuid })); + } +}, { + name: "Advanced", + icon: AdvancedIcon, + execute: (dispatch, resource) => { + dispatch(openFileRemoveDialog(resource.uuid)); + } +}, +{ + name: "Remove", + icon: RemoveIcon, + execute: (dispatch, resource) => { + dispatch(openFileRemoveDialog(resource.uuid)); + } +}]]; diff --git a/src/views-components/context-menu/context-menu.tsx b/src/views-components/context-menu/context-menu.tsx index b6d2b91b..30ecc981 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 fdd8123f..c643fef2 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)( {getUserFullname(user)} + dispatch(openRepositoriesPanel())}>Repositories 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 b0478377..2039f6b8 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) => ({ - {props.buttonVisible ? + {props.buttonVisible && - : null} + } ); diff --git a/src/views/repositories-panel/repositories-panel.tsx b/src/views/repositories-panel/repositories-panel.tsx new file mode 100644 index 00000000..3d2d32c0 --- /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 = (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 => ({ + loadRepositories: () => dispatch(loadRepositoriesData()), + onOptionsMenuOpen: (event) => { + dispatch(openRepositoryContextMenu(event)); + }, +}); + +interface RepositoriesActionProps { + loadRepositories: () => void; + onOptionsMenuOpen: (event: React.MouseEvent) => void; +} + +interface RepositoriesDataProps { + repositories: RepositoriesResource[]; +} + + +type RepositoriesProps = RepositoriesDataProps & RepositoriesActionProps & WithStyles; + +export const RepositoriesPanel = compose( + withStyles(styles), + connect(mapStateToProps, mapDispatchToProps))( + class extends React.Component { + componentDidMount() { + this.props.loadRepositories(); + } + render() { + const { classes, repositories, onOptionsMenuOpen } = this.props; + console.log(repositories); + return ( + + + + + + When you are using an Arvados virtual machine, you should clone the https:// URLs. This will authenticate automatically using your API token.
+ In order to clone git repositories using SSH, add an SSH key to your account and clone the git@ URLs. +
+
+ + + +
+ +
+ + + + + +
+
+ + {repositories && + + + Name + URL + + + + + {repositories.map((repository, index) => + + {repository.name} + {repository.cloneUrls.join("\n")} + + + + + + + + )} + +
} +
+
+
+ ); + } + } + ); \ No newline at end of file diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index 8d1fb670..d0509d0a 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 = + -- 2.30.2