Merge branch '13601-basic-data-exploring-component'
[arvados-workbench2.git] / src / views / workbench / workbench.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6
7 import { StyleRulesCallback, Theme, WithStyles, withStyles } from '@material-ui/core/styles';
8 import Drawer from '@material-ui/core/Drawer';
9 import { connect, DispatchProp } from "react-redux";
10 import { Route, Switch } from "react-router";
11 import authActions from "../../store/auth/auth-action";
12 import { User } from "../../models/user";
13 import { RootState } from "../../store/store";
14 import MainAppBar, { MainAppBarActionProps, MainAppBarMenuItem } from '../../components/main-app-bar/main-app-bar';
15 import { Breadcrumb } from '../../components/breadcrumbs/breadcrumbs';
16 import { push } from 'react-router-redux';
17 import projectActions from "../../store/project/project-action";
18 import ProjectTree from '../../components/project-tree/project-tree';
19 import { TreeItem, TreeItemStatus } from "../../components/tree/tree";
20 import { Project } from "../../models/project";
21 import { projectService } from '../../services/services';
22 import DataExplorer from '../data-explorer/data-explorer';
23
24 const drawerWidth = 240;
25
26 type CssRules = 'root' | 'appBar' | 'drawerPaper' | 'content' | 'toolbar';
27
28 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
29     root: {
30         flexGrow: 1,
31         zIndex: 1,
32         overflow: 'hidden',
33         position: 'relative',
34         display: 'flex',
35         width: '100vw',
36         height: '100vh'
37     },
38     appBar: {
39         zIndex: theme.zIndex.drawer + 1,
40         backgroundColor: '#692498',
41         position: "absolute",
42         width: "100%"
43     },
44     drawerPaper: {
45         position: 'relative',
46         width: drawerWidth,
47     },
48     content: {
49         flexGrow: 1,
50         backgroundColor: theme.palette.background.default,
51         padding: theme.spacing.unit * 3,
52         height: '100%',
53         minWidth: 0,
54     },
55     toolbar: theme.mixins.toolbar
56 });
57
58 interface WorkbenchDataProps {
59     projects: Array<TreeItem<Project>>;
60     user?: User;
61 }
62
63 interface WorkbenchActionProps {
64 }
65
66 type WorkbenchProps = WorkbenchDataProps & WorkbenchActionProps & DispatchProp & WithStyles<CssRules>;
67
68 interface NavBreadcrumb extends Breadcrumb {
69     path: string;
70 }
71
72 interface NavMenuItem extends MainAppBarMenuItem {
73     action: () => void;
74 }
75
76 interface WorkbenchState {
77     anchorEl: any;
78     breadcrumbs: NavBreadcrumb[];
79     searchText: string;
80     menuItems: {
81         accountMenu: NavMenuItem[],
82         helpMenu: NavMenuItem[],
83         anonymousMenu: NavMenuItem[]
84     };
85 }
86
87 class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
88     state = {
89         anchorEl: null,
90         searchText: "",
91         breadcrumbs: [
92             {
93                 label: "Projects",
94                 path: "/projects"
95             }, {
96                 label: "Project 1",
97                 path: "/projects/project-1"
98             }
99         ],
100         menuItems: {
101             accountMenu: [
102                 {
103                     label: "Logout",
104                     action: () => this.props.dispatch(authActions.LOGOUT())
105                 },
106                 {
107                     label: "My account",
108                     action: () => this.props.dispatch(push("/my-account"))
109                 }
110             ],
111             helpMenu: [
112                 {
113                     label: "Help",
114                     action: () => this.props.dispatch(push("/help"))
115                 }
116             ],
117             anonymousMenu: [
118                 {
119                     label: "Sign in",
120                     action: () => this.props.dispatch(authActions.LOGIN())
121                 }
122             ]
123         }
124     };
125
126
127     mainAppBarActions: MainAppBarActionProps = {
128         onBreadcrumbClick: (breadcrumb: NavBreadcrumb) => this.props.dispatch(push(breadcrumb.path)),
129         onSearch: searchText => {
130             this.setState({ searchText });
131             this.props.dispatch(push(`/search?q=${searchText}`));
132         },
133         onMenuItemClick: (menuItem: NavMenuItem) => menuItem.action()
134     };
135
136     toggleProjectTreeItem = (itemId: string, status: TreeItemStatus) => {
137         if (status === TreeItemStatus.Loaded) {
138             this.openProjectItem(itemId);
139         } else {
140             this.props.dispatch<any>(projectService.getProjectList(itemId)).then(() => this.openProjectItem(itemId));
141         }
142     }
143
144     openProjectItem = (itemId: string) => {
145         this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId));
146         this.props.dispatch(push(`/project/${itemId}`));
147     }
148
149     render() {
150         const { classes, user } = this.props;
151         return (
152             <div className={classes.root}>
153                 <div className={classes.appBar}>
154                     <MainAppBar
155                         breadcrumbs={this.state.breadcrumbs}
156                         searchText={this.state.searchText}
157                         user={this.props.user}
158                         menuItems={this.state.menuItems}
159                         {...this.mainAppBarActions}
160                     />
161                 </div>
162                 {user &&
163                     <Drawer
164                         variant="permanent"
165                         classes={{
166                             paper: classes.drawerPaper,
167                         }}>
168                         <div className={classes.toolbar} />
169                         <ProjectTree
170                             projects={this.props.projects}
171                             toggleProjectTreeItem={this.toggleProjectTreeItem} />
172                     </Drawer>}
173                 <main className={classes.content}>
174                     <div className={classes.toolbar} />
175                     <div className={classes.toolbar} />
176                     <Switch>
177                         <Route path="/project/:name" component={DataExplorer} />
178                     </Switch>
179                 </main>
180             </div>
181         );
182     }
183 }
184
185 export default connect<WorkbenchDataProps>(
186     (state: RootState) => ({
187         projects: state.projects,
188         user: state.auth.user
189     })
190 )(
191     withStyles(styles)(Workbench)
192 );