[ARVADOS-WORKBENCH2] created: 1.1.4-427-gc01f423

Git user git at public.curoverse.com
Fri Jul 27 07:00:32 EDT 2018


        at  c01f42311966c4d13413ee34dd6c97d5e5ac8b7f (commit)


commit c01f42311966c4d13413ee34dd6c97d5e5ac8b7f
Author: Janicki Artur <artur.janicki at contractors.roche.com>
Date:   Fri Jul 27 13:00:16 2018 +0200

    init collection view with routing and store
    
    Feature #13853
    
    Arvados-DCO-1.1-Signed-off-by: Janicki Artur <artur.janicki at contractors.roche.com>

diff --git a/src/common/actions.ts b/src/common/actions.ts
new file mode 100644
index 0000000..9ab6fd4
--- /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 c85acd9..e5d2e5e 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 e80fee8..cc3108a 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) => <StarBorder {...props} />;
 export const AdvancedIcon: IconType = (props) => <Folder {...props} />;
 export const CustomizeTableIcon: IconType = (props) => <Menu {...props} />;
 export const CopyIcon: IconType = (props) => <ContentCopy {...props} />;
-export const CollectionIcon: IconType = (props) => <Folder {...props} />;
+export const CollectionIcon: IconType = (props) => <LibraryBooks {...props} />;
 export const CloseIcon: IconType = (props) => <Close {...props} />;
 export const DefaultIcon: IconType = (props) => <RateReview {...props} />;
 export const DetailsIcon: IconType = (props) => <Info {...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 0000000..c2684e0
--- /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<typeof collectionPanelActions>;
+
+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 0000000..0dd233e
--- /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 50f6e20..0cb6983 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 = <T extends Resource>(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 ae07744..c5845d4 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 0000000..99c4e00
--- /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<CssRules> = (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<CssRules> & RouteComponentProps<{ id: string }>;
+
+export const CollectionPanel = withStyles(styles)(
+    connect((state: RootState) => ({ item: state.collectionPanel.item }))(
+        class extends React.Component<CollectionPanelProps> { 
+
+            state = {
+                anchorEl: undefined
+            };
+
+            showMenu = (event: any) => {
+                this.setState({ anchorEl: event.currentTarget });
+            }
+
+            closeMenu = () => {
+                this.setState({ anchorEl: undefined });
+            }
+
+            displayMenuAction = () => {
+                return <IconButton
+                    aria-label="More options"
+                    aria-owns={this.state.anchorEl ? 'submenu' : undefined}
+                    aria-haspopup="true"
+                    onClick={this.showMenu}>
+                    <MoreOptionsIcon />
+                </IconButton>;
+            }
+
+            render() {
+                const { anchorEl } = this.state;
+                const { classes, item } = this.props;
+                return <div>
+                        <Card className={classes.card}>
+                            <CardHeader 
+                                avatar={ <CollectionIcon className={classes.iconHeader} /> }
+                                action={ 
+                                    <IconButton
+                                        aria-label="More options"
+                                        aria-owns={anchorEl ? 'submenu' : undefined}
+                                        aria-haspopup="true"
+                                        onClick={this.showMenu}>
+                                        <MoreOptionsIcon />
+                                    </IconButton> 
+                                }
+                                title={item && item.name } />
+                            <CardContent>
+                                <Menu
+                                    id="submenu"
+                                    anchorEl={anchorEl}
+                                    open={Boolean(anchorEl)}
+                                    onClose={this.closeMenu}>
+                                    {MENU_OPTIONS.map((option) => (
+                                        <MenuItem key={option.title}>
+                                            <ListItemIcon>
+                                                <option.icon />
+                                            </ListItemIcon>
+                                            <ListItemText inset primary={
+                                                <Typography variant='body1'>
+                                                    {option.title}
+                                                </Typography>
+                                            }/>
+                                        </MenuItem>
+                                    ))}
+                                </Menu>
+                                <Grid container direction="column">
+                                    <Grid item xs={6}>
+                                    <DetailsAttribute label='Collection UUID' value={item && item.uuid} />
+                                        <DetailsAttribute label='Content size' value='54 MB' />
+                                    <DetailsAttribute label='Owner' value={item && item.ownerUuid} />
+                                    </Grid>
+                                </Grid>
+                            </CardContent>
+                        </Card>
+
+                        <Card className={classes.card}>
+                            <CardHeader title="Tags" />
+                            <CardContent>
+                                <Grid container direction="column">
+                                    <Grid item xs={4}>
+                                        Tags
+                                    </Grid>
+                                </Grid>
+                            </CardContent>
+                        </Card>
+
+                        <Card className={classes.card}>
+                            <CardHeader title="Files" />
+                            <CardContent>
+                                <Grid container direction="column">
+                                    <Grid item xs={4}>
+                                        Tags
+                                    </Grid>
+                                </Grid>
+                            </CardContent>
+                        </Card>
+                    </div>;
+            }
+
+            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 c2b42a5..2ddc1b0 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)}
         </Grid>
         <Grid item>
-            <Typography color="primary">
+            <Typography color="default">
                 {item.name}
             </Typography>
         </Grid>
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 8e2cb57..f39c779 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)(
                                 <Switch>
                                     <Route path="/projects/:id" render={this.renderProjectPanel} />
                                     <Route path="/favorites" render={this.renderFavoritePanel} />
+                                    <Route path="/collections/:id" render={this.renderCollectionPanel} />
                                 </Switch>
                             </div>
                             {user && <DetailsPanel />}
@@ -227,6 +231,10 @@ export const Workbench = withStyles(styles)(
                 );
             }
 
+            renderCollectionPanel = (props: RouteComponentProps<{ id: string }>) => <CollectionPanel 
+                onItemRouteChange={(collectionId) => this.props.dispatch<any>(loadCollection(collectionId, ResourceKind.Collection))}
+                {...props} />
+
             renderProjectPanel = (props: RouteComponentProps<{ id: string }>) => <ProjectPanel
                 onItemRouteChange={itemId => this.props.dispatch<any>(setProjectItem(itemId, ItemMode.ACTIVE))}
                 onContextMenu={(event, item) => {
@@ -242,8 +250,14 @@ export const Workbench = withStyles(styles)(
                     this.props.dispatch<any>(loadDetails(item.uuid, item.kind as ResourceKind));
                 }}
                 onItemDoubleClick={item => {
-                    this.props.dispatch<any>(setProjectItem(item.uuid, ItemMode.ACTIVE));
-                    this.props.dispatch<any>(loadDetails(item.uuid, ResourceKind.Project));
+                    switch (item.kind) {
+                        case ResourceKind.Collection:
+                            this.props.dispatch<any>(loadCollection(item.uuid, item.kind as ResourceKind));
+                            this.props.dispatch(push(goToCollection(item.uuid)));
+                        default: 
+                            this.props.dispatch<any>(setProjectItem(item.uuid, ItemMode.ACTIVE));
+                            this.props.dispatch<any>(loadDetails(item.uuid, item.kind as ResourceKind));
+                    }
                 }}
                 {...props} />
 

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list