From: Michal Klobukowski Date: Tue, 10 Jul 2018 09:06:49 +0000 (+0200) Subject: Create store and connect detail panel X-Git-Tag: 1.2.0~54^2~3 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/7622261116e685cad4af9cf6a4d1dd9c58cd1605 Create store and connect detail panel Feature #13765 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- diff --git a/src/store/details-panel/details-panel-action.ts b/src/store/details-panel/details-panel-action.ts new file mode 100644 index 00000000..e2d2479b --- /dev/null +++ b/src/store/details-panel/details-panel-action.ts @@ -0,0 +1,35 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { unionize, ofType, UnionOf } from "unionize"; +import { Resource } from "../../common/api/common-resource-service"; +import { ResourceKind } from "../../models/kinds"; +import { Dispatch } from "redux"; +import { groupsService } from "../../services/services"; + +const actions = unionize({ + TOGGLE_DETAILS_PANEL: ofType<{}>(), + LOAD_DETAILS: ofType<{ uuid: string, kind: ResourceKind }>(), + LOAD_DETAILS_SUCCESS: ofType<{ item: Resource }>(), +}, { tag: 'type', value: 'payload' }); + +export default actions; + +export type DetailsPanelAction = UnionOf; + +export const loadDetails = (uuid: string, kind: ResourceKind) => + (dispatch: Dispatch) => { + dispatch(actions.LOAD_DETAILS({ uuid, kind })); + if (kind === ResourceKind.Project) { + groupsService + .get(uuid) + .then(project => { + dispatch(actions.LOAD_DETAILS_SUCCESS({ item: project })); + }); + } + + }; + + + diff --git a/src/store/details-panel/details-panel-reducer.ts b/src/store/details-panel/details-panel-reducer.ts new file mode 100644 index 00000000..73fc604d --- /dev/null +++ b/src/store/details-panel/details-panel-reducer.ts @@ -0,0 +1,26 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { Resource } from "../../common/api/common-resource-service"; +import actions, { DetailsPanelAction } from "./details-panel-action"; + +export interface DetailsPanelState { + item: Resource | null; + isOpened: boolean; +} + +const initialState = { + item: null, + isOpened: false +}; + +const reducer = (state: DetailsPanelState = initialState, action: DetailsPanelAction) => + actions.match(action, { + default: () => state, + LOAD_DETAILS: () => state, + LOAD_DETAILS_SUCCESS: ({ item }) => ({ ...state, item }), + TOGGLE_DETAILS_PANEL: () => ({ ...state, isOpened: !state.isOpened }) + }); + +export default reducer; diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts index 5fb6b729..034cdacc 100644 --- a/src/store/navigation/navigation-action.ts +++ b/src/store/navigation/navigation-action.ts @@ -7,17 +7,19 @@ import projectActions, { getProjectList } from "../project/project-action"; import { push } from "react-router-redux"; import { TreeItemStatus } from "../../components/tree/tree"; import { findTreeItem } from "../project/project-reducer"; -import { Resource, ResourceKind } from "../../models/resource"; +import { Resource, ResourceKind as R } from "../../models/resource"; import sidePanelActions from "../side-panel/side-panel-action"; import dataExplorerActions from "../data-explorer/data-explorer-action"; import { PROJECT_PANEL_ID } from "../../views/project-panel/project-panel"; import { RootState } from "../store"; import { sidePanelData } from "../side-panel/side-panel-reducer"; +import { loadDetails } from "../details-panel/details-panel-action"; +import { ResourceKind } from "../../models/kinds"; export const getResourceUrl = (resource: Resource): string => { switch (resource.kind) { - case ResourceKind.PROJECT: return `/projects/${resource.uuid}`; - case ResourceKind.COLLECTION: return `/collections/${resource.uuid}`; + case R.PROJECT: return `/projects/${resource.uuid}`; + case R.COLLECTION: return `/collections/${resource.uuid}`; default: return ""; } }; @@ -34,6 +36,8 @@ export const setProjectItem = (itemId: string, itemMode: ItemMode) => const treeItem = findTreeItem(projects.items, itemId); if (treeItem) { + // TODO: Get correct resource kind + dispatch(loadDetails(treeItem.data.uuid, ResourceKind.Project)); dispatch(sidePanelActions.RESET_SIDE_PANEL_ACTIVITY()); const projectsItem = sidePanelData[0]; diff --git a/src/store/store.ts b/src/store/store.ts index 00c2ad75..f74b8773 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -13,6 +13,7 @@ import authReducer, { AuthState } from "./auth/auth-reducer"; import dataExplorerReducer, { DataExplorerState } from './data-explorer/data-explorer-reducer'; import collectionsReducer, { CollectionState } from "./collection/collection-reducer"; import { projectPanelMiddleware } from '../store/project-panel/project-panel-middleware'; +import detailsPanelReducer, { DetailsPanelState } from './details-panel/details-panel-reducer'; const composeEnhancers = (process.env.NODE_ENV === 'development' && @@ -26,6 +27,7 @@ export interface RootState { router: RouterState; dataExplorer: DataExplorerState; sidePanel: SidePanelState; + detailsPanel: DetailsPanelState; } const rootReducer = combineReducers({ @@ -34,7 +36,8 @@ const rootReducer = combineReducers({ collections: collectionsReducer, router: routerReducer, dataExplorer: dataExplorerReducer, - sidePanel: sidePanelReducer + sidePanel: sidePanelReducer, + detailsPanel: detailsPanelReducer }); diff --git a/src/views-components/details-panel/details-panel.tsx b/src/views-components/details-panel/details-panel.tsx index f47dfa06..13498f33 100644 --- a/src/views-components/details-panel/details-panel.tsx +++ b/src/views-components/details-panel/details-panel.tsx @@ -13,13 +13,20 @@ import Tab from '@material-ui/core/Tab'; import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; import * as classnames from "classnames"; +import { connect, Dispatch } from 'react-redux'; import EmptyState from '../../components/empty-state/empty-state'; import IconBase from '../../components/icon/icon'; +import { RootState } from '../../store/store'; +import actions from "../../store/details-panel/details-panel-action"; +import { Resource } from '../../common/api/common-resource-service'; +import { ResourceKind } from '../../models/kinds'; +import { ProjectResource } from '../../models/project'; +import { CollectionResource } from '../../models/collection'; export interface DetailsPanelDataProps { onCloseDrawer: () => void; isOpened: boolean; - renderHeader?: React.ComponentType<{}>; + header: React.ReactElement; renderDetails?: React.ComponentType<{}>; renderActivity?: React.ComponentType<{}>; } @@ -27,47 +34,41 @@ export interface DetailsPanelDataProps { type DetailsPanelProps = DetailsPanelDataProps & WithStyles; class DetailsPanel extends React.Component { - state = { + state = { tabsValue: 0 - }; + }; - handleChange = (event: any, value: boolean) => { - this.setState({ tabsValue: value }); - } - - renderTabContainer = (children: React.ReactElement) => + handleChange = (event: any, value: boolean) => { + this.setState({ tabsValue: value }); + } + + renderTabContainer = (children: React.ReactElement) => {children} - - render() { - const { classes, onCloseDrawer, isOpened, renderHeader, renderDetails, renderActivity } = this.props; + + render() { + const { classes, onCloseDrawer, isOpened, header, renderDetails, renderActivity } = this.props; const { tabsValue } = this.state; return ( - - - {renderHeader} - {/* TODO: renderHeader */} - - - Tutorial pipeline - - {/* End */} + + + {header} - - - - - - - + + + + + + + {tabsValue === 0 && this.renderTabContainer( {renderDetails} - @@ -77,14 +78,14 @@ class DetailsPanel extends React.Component { - - )} + + )} {tabsValue === 1 && this.renderTabContainer( {renderActivity} - )} + )} ); @@ -96,9 +97,9 @@ type CssRules = 'drawerPaper' | 'container' | 'opened' | 'headerContainer' | 'ta const drawerWidth = 320; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ - container: { + container: { width: 0, - position: 'relative', + position: 'relative', height: 'auto', transition: 'width 0.5s ease', '&$opened': { @@ -109,18 +110,58 @@ const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ drawerPaper: { position: 'relative', width: drawerWidth - }, - headerContainer: { + }, + headerContainer: { color: theme.palette.grey["600"], margin: `${theme.spacing.unit}px 0`, '& .fa-cogs': { fontSize: "24px", color: theme.customs.colors.green700 } - }, - tabContainer: { - padding: theme.spacing.unit * 3 - } + }, + tabContainer: { + padding: theme.spacing.unit * 3 + } }); -export default withStyles(styles)(DetailsPanel); \ No newline at end of file +const renderCollectionHeader = (collection: CollectionResource) => + <> + + + {collection.name} + + ; + +const renderProjectHeader = (project: ProjectResource) => + <> + + + {project.name} + + ; + +const renderHeader = (resource: Resource) => { + switch(resource.kind) { + case ResourceKind.Project: + return renderProjectHeader(resource as ProjectResource); + case ResourceKind.Collection: + return renderCollectionHeader(resource as CollectionResource); + default: + return null; + } +}; + +const mapStateToProps = ({detailsPanel}: RootState) => ({ + isOpened: detailsPanel.isOpened, + header: detailsPanel.item ? renderHeader(detailsPanel.item) : null +}); + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + onCloseDrawer: () => { + dispatch(actions.TOGGLE_DETAILS_PANEL()); + } +}); + +const DetailsPanelContainer = connect(mapStateToProps, mapDispatchToProps)(DetailsPanel); + +export default withStyles(styles)(DetailsPanelContainer); \ No newline at end of file diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index 8315d5b0..e8487845 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -6,9 +6,8 @@ import * as React from 'react'; import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles'; import Drawer from '@material-ui/core/Drawer'; import { connect, DispatchProp } from "react-redux"; -import { Route, Switch, RouteComponentProps, withRouter } from "react-router"; +import { Route, Switch, RouteComponentProps } from "react-router"; import authActions from "../../store/auth/auth-action"; -import dataExplorerActions from "../../store/data-explorer/data-explorer-action"; import { User } from "../../models/user"; import { RootState } from "../../store/store"; import MainAppBar, { @@ -20,16 +19,15 @@ import { push } from 'react-router-redux'; import ProjectTree from '../../views-components/project-tree/project-tree'; import { TreeItem } from "../../components/tree/tree"; import { Project } from "../../models/project"; -import { getTreePath, findTreeItem } from '../../store/project/project-reducer'; +import { getTreePath } from '../../store/project/project-reducer'; import sidePanelActions from '../../store/side-panel/side-panel-action'; import SidePanel, { SidePanelItem } from '../../components/side-panel/side-panel'; -import { ResourceKind } from "../../models/resource"; import { ItemMode, setProjectItem } from "../../store/navigation/navigation-action"; import projectActions from "../../store/project/project-action"; import ProjectPanel from "../project-panel/project-panel"; -import { sidePanelData } from '../../store/side-panel/side-panel-reducer'; import DetailsPanel from '../../views-components/details-panel/details-panel'; import { ArvadosTheme } from '../../common/custom-theme'; +import detailsPanelActions from "../../store/details-panel/details-panel-action"; const drawerWidth = 240; const appBarHeight = 100; @@ -100,10 +98,6 @@ interface WorkbenchState { helpMenu: NavMenuItem[], anonymousMenu: NavMenuItem[] }; - isDetailsPanelOpened: boolean; - detailsPanelHeader: React.ComponentType<{}> | undefined; - detailsPanelDetails: React.ComponentType<{}> | undefined; - detailsPanelActivity: React.ComponentType<{}> | undefined; } @@ -135,11 +129,7 @@ class Workbench extends React.Component { action: () => this.props.dispatch(authActions.LOGIN()) } ] - }, - isDetailsPanelOpened: false, - detailsPanelHeader: undefined, - detailsPanelDetails: undefined, - detailsPanelActivity: undefined, + } }; mainAppBarActions: MainAppBarActionProps = { @@ -152,7 +142,7 @@ class Workbench extends React.Component { }, onMenuItemClick: (menuItem: NavMenuItem) => menuItem.action(), onDetailsPanelToggle: () => { - this.setState(prev => ({ isDetailsPanelOpened: !prev.isDetailsPanelOpened })); + this.props.dispatch(detailsPanelActions.TOGGLE_DETAILS_PANEL()); } }; @@ -209,12 +199,7 @@ class Workbench extends React.Component { - + );