From c01f42311966c4d13413ee34dd6c97d5e5ac8b7f Mon Sep 17 00:00:00 2001 From: Janicki Artur Date: Fri, 27 Jul 2018 13:00:16 +0200 Subject: [PATCH] init collection view with routing and store Feature #13853 Arvados-DCO-1.1-Signed-off-by: Janicki Artur --- src/common/actions.ts | 11 ++ src/common/custom-theme.ts | 28 ++- src/components/icon/icon.tsx | 3 +- .../collection-panel-action.ts | 30 +++ .../collection-panel-reducer.ts | 21 ++ src/store/navigation/navigation-action.ts | 5 +- src/store/store.ts | 3 + .../collection-panel/collection-panel.tsx | 187 ++++++++++++++++++ src/views/project-panel/project-panel.tsx | 2 +- src/views/workbench/workbench.tsx | 18 +- 10 files changed, 299 insertions(+), 9 deletions(-) create mode 100644 src/common/actions.ts create mode 100644 src/store/collection-panel/collection-panel-action.ts create mode 100644 src/store/collection-panel/collection-panel-reducer.ts create mode 100644 src/views/collection-panel/collection-panel.tsx diff --git a/src/common/actions.ts b/src/common/actions.ts new file mode 100644 index 00000000..9ab6fd48 --- /dev/null +++ b/src/common/actions.ts @@ -0,0 +1,11 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +export const goToProject = (uuid: string) => { + return `/projects/${uuid}`; +}; + +export const goToCollection = (uuid: string) => { + return `/collections/${uuid}`; +}; \ No newline at end of file diff --git a/src/common/custom-theme.ts b/src/common/custom-theme.ts index c85acd90..e5d2e5e7 100644 --- a/src/common/custom-theme.ts +++ b/src/common/custom-theme.ts @@ -16,11 +16,17 @@ interface ArvadosThemeOptions extends ThemeOptions { } export interface ArvadosTheme extends Theme { - customs: any; + customs: { + colors: Colors + }; +} + +interface Colors { + green700: string; + yellow700: string; } const red900 = red["900"]; -const yellow700 = yellow["700"]; const purple800 = purple["800"]; const grey200 = grey["200"]; const grey300 = grey["300"]; @@ -32,7 +38,8 @@ const grey900 = grey["900"]; const themeOptions: ArvadosThemeOptions = { customs: { colors: { - green700: green["700"] + green700: green["700"], + yellow700: yellow["700"] } }, overrides: { @@ -74,6 +81,21 @@ const themeOptions: ArvadosThemeOptions = { root: { fontSize: '1.25rem' } + }, + MuiCardHeader: { + avatar: { + display: 'flex', + alignItems: 'center' + }, + title: { + color: grey700, + fontSize: '1.25rem' + } + }, + MuiMenuItem: { + root: { + padding: '8px 16px' + } } }, mixins: { diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx index e80fee8e..cc3108ad 100644 --- a/src/components/icon/icon.tsx +++ b/src/components/icon/icon.tsx @@ -21,6 +21,7 @@ import Help from '@material-ui/icons/Help'; import Inbox from '@material-ui/icons/Inbox'; import Info from '@material-ui/icons/Info'; import Input from '@material-ui/icons/Input'; +import LibraryBooks from '@material-ui/icons/LibraryBooks'; import Menu from '@material-ui/icons/Menu'; import MoreVert from '@material-ui/icons/MoreVert'; import Notifications from '@material-ui/icons/Notifications'; @@ -39,7 +40,7 @@ export const AddFavoriteIcon: IconType = (props) => ; export const AdvancedIcon: IconType = (props) => ; export const CustomizeTableIcon: IconType = (props) => ; export const CopyIcon: IconType = (props) => ; -export const CollectionIcon: IconType = (props) => ; +export const CollectionIcon: IconType = (props) => ; export const CloseIcon: IconType = (props) => ; export const DefaultIcon: IconType = (props) => ; export const DetailsIcon: IconType = (props) => ; diff --git a/src/store/collection-panel/collection-panel-action.ts b/src/store/collection-panel/collection-panel-action.ts new file mode 100644 index 00000000..c2684e05 --- /dev/null +++ b/src/store/collection-panel/collection-panel-action.ts @@ -0,0 +1,30 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { unionize, ofType, UnionOf } from "unionize"; +import { CommonResourceService } from "../../common/api/common-resource-service"; +import { Dispatch } from "redux"; +import { serverApi } from "../../common/api/server-api"; +import { ResourceKind } from "../../models/resource"; +import { CollectionResource } from "../../models/collection"; + +export const collectionPanelActions = unionize({ + LOAD_COLLECTION: ofType<{ uuid: string, kind: ResourceKind }>(), + LOAD_COLLECTION_SUCCESS: ofType<{ item: CollectionResource }>(), +}, { tag: 'type', value: 'payload' }); + +export type CollectionPanelAction = UnionOf; + +export const loadCollection = (uuid: string, kind: ResourceKind) => + (dispatch: Dispatch) => { + dispatch(collectionPanelActions.LOAD_COLLECTION({ uuid, kind })); + return new CommonResourceService(serverApi, "collections") + .get(uuid) + .then(item => { + dispatch(collectionPanelActions.LOAD_COLLECTION_SUCCESS({ item: item as CollectionResource })); + }); + }; + + + diff --git a/src/store/collection-panel/collection-panel-reducer.ts b/src/store/collection-panel/collection-panel-reducer.ts new file mode 100644 index 00000000..0dd233ea --- /dev/null +++ b/src/store/collection-panel/collection-panel-reducer.ts @@ -0,0 +1,21 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { collectionPanelActions, CollectionPanelAction } from "./collection-panel-action"; +import { CollectionResource } from "../../models/collection"; + +export interface CollectionPanelState { + item: CollectionResource | null; +} + +const initialState = { + item: null +}; + +export const collectionPanelReducer = (state: CollectionPanelState = initialState, action: CollectionPanelAction) => + collectionPanelActions.match(action, { + default: () => state, + LOAD_COLLECTION: () => state, + LOAD_COLLECTION_SUCCESS: ({ item }) => ({ ...state, item }), + }); diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts index 50f6e205..0cb69831 100644 --- a/src/store/navigation/navigation-action.ts +++ b/src/store/navigation/navigation-action.ts @@ -11,11 +11,12 @@ import { dataExplorerActions } from "../data-explorer/data-explorer-action"; import { PROJECT_PANEL_ID } from "../../views/project-panel/project-panel"; import { RootState } from "../store"; import { Resource, ResourceKind } from "../../models/resource"; +import { goToProject, goToCollection } from "../../common/actions"; export const getResourceUrl = (resource: T): string => { switch (resource.kind) { - case ResourceKind.Project: return `/projects/${resource.uuid}`; - case ResourceKind.Collection: return `/collections/${resource.uuid}`; + case ResourceKind.Project: return goToProject(resource.uuid); + case ResourceKind.Collection: return goToCollection(resource.uuid); default: return resource.href; } }; diff --git a/src/store/store.ts b/src/store/store.ts index ae077442..c5845d48 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -18,6 +18,7 @@ import { favoritePanelMiddleware } from "./favorite-panel/favorite-panel-middlew import { reducer as formReducer } from 'redux-form'; import { FavoritesState, favoritesReducer } from './favorites/favorites-reducer'; import { snackbarReducer, SnackbarState } from './snackbar/snackbar-reducer'; +import { CollectionPanelState, collectionPanelReducer } from './collection-panel/collection-panel-reducer'; const composeEnhancers = (process.env.NODE_ENV === 'development' && @@ -30,6 +31,7 @@ export interface RootState { router: RouterState; dataExplorer: DataExplorerState; sidePanel: SidePanelState; + collectionPanel: CollectionPanelState; detailsPanel: DetailsPanelState; contextMenu: ContextMenuState; favorites: FavoritesState; @@ -42,6 +44,7 @@ const rootReducer = combineReducers({ router: routerReducer, dataExplorer: dataExplorerReducer, sidePanel: sidePanelReducer, + collectionPanel: collectionPanelReducer, detailsPanel: detailsPanelReducer, contextMenu: contextMenuReducer, form: formReducer, diff --git a/src/views/collection-panel/collection-panel.tsx b/src/views/collection-panel/collection-panel.tsx new file mode 100644 index 00000000..99c4e00a --- /dev/null +++ b/src/views/collection-panel/collection-panel.tsx @@ -0,0 +1,187 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from 'react'; +import { + StyleRulesCallback, WithStyles, withStyles, Card, CardHeader, IconButton, + CardContent, Grid, MenuItem, Menu, ListItemIcon, ListItemText, Typography +} from '@material-ui/core'; +import { connect } from 'react-redux'; +import { RouteComponentProps } from 'react-router'; +import { ArvadosTheme } from '../../common/custom-theme'; +import { RootState } from '../../store/store'; +import { + MoreOptionsIcon, CollectionIcon, ShareIcon, RenameIcon, AddFavoriteIcon, MoveToIcon, + CopyIcon, ProvenanceGraphIcon, DetailsIcon, AdvancedIcon, RemoveIcon +} from '../../components/icon/icon'; +import { DetailsAttribute } from '../../components/details-attribute/details-attribute'; +import { CollectionResource } from '../../models/collection'; + +type CssRules = 'card' | 'iconHeader'; + +const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ + card: { + marginBottom: '20px' + }, + iconHeader: { + fontSize: '1.875rem', + color: theme.customs.colors.yellow700 + } +}); + +const MENU_OPTIONS = [ + { + title: 'Edit collection', + icon: RenameIcon + }, + { + title: 'Share', + icon: ShareIcon + }, + { + title: 'Move to', + icon: MoveToIcon + }, + { + title: 'Add to favorites', + icon: AddFavoriteIcon + }, + { + title: 'Copy to project', + icon: CopyIcon + }, + { + title: 'View details', + icon: DetailsIcon + }, + { + title: 'Provenance graph', + icon: ProvenanceGraphIcon + }, + { + title: 'Advanced', + icon: AdvancedIcon + }, + { + title: 'Remove', + icon: RemoveIcon + } +]; + +interface CollectionPanelDataProps { + item: CollectionResource; +} + +interface CollectionPanelActionProps { + onItemRouteChange: (collectionId: string) => void; +} + +type CollectionPanelProps = CollectionPanelDataProps & CollectionPanelActionProps + & WithStyles & RouteComponentProps<{ id: string }>; + +export const CollectionPanel = withStyles(styles)( + connect((state: RootState) => ({ item: state.collectionPanel.item }))( + class extends React.Component { + + state = { + anchorEl: undefined + }; + + showMenu = (event: any) => { + this.setState({ anchorEl: event.currentTarget }); + } + + closeMenu = () => { + this.setState({ anchorEl: undefined }); + } + + displayMenuAction = () => { + return + + ; + } + + render() { + const { anchorEl } = this.state; + const { classes, item } = this.props; + return
+ + } + action={ + + + + } + title={item && item.name } /> + + + {MENU_OPTIONS.map((option) => ( + + + + + + {option.title} + + }/> + + ))} + + + + + + + + + + + + + + + + + Tags + + + + + + + + + + + Tags + + + + +
; + } + + componentWillReceiveProps({ match, item, onItemRouteChange }: CollectionPanelProps) { + if (!item || match.params.id !== item.uuid) { + onItemRouteChange(match.params.id); + } + } + + } + ) +); \ No newline at end of file diff --git a/src/views/project-panel/project-panel.tsx b/src/views/project-panel/project-panel.tsx index c2b42a55..2ddc1b05 100644 --- a/src/views/project-panel/project-panel.tsx +++ b/src/views/project-panel/project-panel.tsx @@ -38,7 +38,7 @@ const renderName = (item: ProjectPanelItem) => {renderIcon(item)} - + {item.name} diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index 8e2cb578..f39c7792 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -36,6 +36,9 @@ import { FavoritePanel, FAVORITE_PANEL_ID } from "../favorite-panel/favorite-pan import { CurrentTokenDialog } from '../../views-components/current-token-dialog/current-token-dialog'; import { dataExplorerActions } from '../../store/data-explorer/data-explorer-action'; import { Snackbar } from '../../views-components/snackbar/snackbar'; +import { CollectionPanel } from '../collection-panel/collection-panel'; +import { goToCollection } from '../../common/actions'; +import { loadCollection } from '../../store/collection-panel/collection-panel-action'; const drawerWidth = 240; const appBarHeight = 100; @@ -212,6 +215,7 @@ export const Workbench = withStyles(styles)( + {user && } @@ -227,6 +231,10 @@ export const Workbench = withStyles(styles)( ); } + renderCollectionPanel = (props: RouteComponentProps<{ id: string }>) => this.props.dispatch(loadCollection(collectionId, ResourceKind.Collection))} + {...props} /> + renderProjectPanel = (props: RouteComponentProps<{ id: string }>) => this.props.dispatch(setProjectItem(itemId, ItemMode.ACTIVE))} onContextMenu={(event, item) => { @@ -242,8 +250,14 @@ export const Workbench = withStyles(styles)( this.props.dispatch(loadDetails(item.uuid, item.kind as ResourceKind)); }} onItemDoubleClick={item => { - this.props.dispatch(setProjectItem(item.uuid, ItemMode.ACTIVE)); - this.props.dispatch(loadDetails(item.uuid, ResourceKind.Project)); + switch (item.kind) { + case ResourceKind.Collection: + this.props.dispatch(loadCollection(item.uuid, item.kind as ResourceKind)); + this.props.dispatch(push(goToCollection(item.uuid))); + default: + this.props.dispatch(setProjectItem(item.uuid, ItemMode.ACTIVE)); + this.props.dispatch(loadDetails(item.uuid, item.kind as ResourceKind)); + } }} {...props} /> -- 2.30.2