Merge branch '13777-double-click-handler-in-project-panel'
[arvados-workbench2.git] / src / views / project-panel / project-panel.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 import { ProjectPanelItem } from './project-panel-item';
7 import { Grid, Typography, Button, Toolbar, StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
8 import { formatDate, formatFileSize } from '../../common/formatters';
9 import DataExplorer from "../../views-components/data-explorer/data-explorer";
10 import { ContextMenuAction } from '../../components/context-menu/context-menu';
11 import { DispatchProp, connect } from 'react-redux';
12 import { DataColumns } from '../../components/data-table/data-table';
13 import { RouteComponentProps } from 'react-router';
14 import { RootState } from '../../store/store';
15 import { ResourceKind } from '../../models/kinds';
16 import { DataTableFilterItem } from '../../components/data-table-filters/data-table-filters';
17 import { ContainerRequestState } from '../../models/container-request';
18 import { SortDirection } from '../../components/data-table/data-column';
19
20 export const PROJECT_PANEL_ID = "projectPanel";
21
22 export interface ProjectPanelFilter extends DataTableFilterItem {
23     type: ResourceKind | ContainerRequestState;
24 }
25
26 type ProjectPanelProps = {
27     currentItemId: string,
28     onItemClick: (item: ProjectPanelItem) => void,
29     onItemDoubleClick: (item: ProjectPanelItem) => void,
30     onItemRouteChange: (itemId: string) => void
31 }
32     & DispatchProp
33     & WithStyles<CssRules>
34     & RouteComponentProps<{ id: string }>;
35 class ProjectPanel extends React.Component<ProjectPanelProps> {
36     render() {
37         return <div>
38             <div className={this.props.classes.toolbar}>
39                 <Button color="primary" variant="raised" className={this.props.classes.button}>
40                     Create a collection
41                 </Button>
42                 <Button color="primary" variant="raised" className={this.props.classes.button}>
43                     Run a process
44                 </Button>
45                 <Button color="primary" variant="raised" className={this.props.classes.button}>
46                     Create a project
47                 </Button>
48             </div>
49             <DataExplorer
50                 id={PROJECT_PANEL_ID}
51                 contextActions={contextMenuActions}
52                 onRowClick={this.props.onItemClick}
53                 onRowDoubleClick={this.props.onItemDoubleClick}
54                 onContextAction={this.executeAction} />;
55         </div>;
56     }
57
58     componentWillReceiveProps({ match, currentItemId }: ProjectPanelProps) {
59         if (match.params.id !== currentItemId) {
60             this.props.onItemRouteChange(match.params.id);
61         }
62     }
63
64     executeAction = (action: ContextMenuAction, item: ProjectPanelItem) => {
65         alert(`Executing ${action.name} on ${item.name}`);
66     }
67
68 }
69
70 type CssRules = "toolbar" | "button";
71
72 const styles: StyleRulesCallback<CssRules> = theme => ({
73     toolbar: {
74         paddingBottom: theme.spacing.unit * 3,
75         textAlign: "right"
76     },
77     button: {
78         marginLeft: theme.spacing.unit
79     }
80 });
81
82 const renderName = (item: ProjectPanelItem) =>
83     <Grid
84         container
85         alignItems="center"
86         wrap="nowrap"
87         spacing={16}>
88         <Grid item>
89             {renderIcon(item)}
90         </Grid>
91         <Grid item>
92             <Typography color="primary">
93                 {item.name}
94             </Typography>
95         </Grid>
96     </Grid>;
97
98
99 const renderIcon = (item: ProjectPanelItem) => {
100     switch (item.kind) {
101         case ResourceKind.Project:
102             return <i className="fas fa-folder fa-lg" />;
103         case ResourceKind.Collection:
104             return <i className="fas fa-archive fa-lg" />;
105         case ResourceKind.Process:
106             return <i className="fas fa-cogs fa-lg" />;
107         default:
108             return <i />;
109     }
110 };
111
112 const renderDate = (date: string) =>
113     <Typography noWrap>
114         {formatDate(date)}
115     </Typography>;
116
117 const renderFileSize = (fileSize?: number) =>
118     <Typography noWrap>
119         {formatFileSize(fileSize)}
120     </Typography>;
121
122 const renderOwner = (owner: string) =>
123     <Typography noWrap color="primary">
124         {owner}
125     </Typography>;
126
127
128
129 const typeToLabel = (type: string) => {
130     switch (type) {
131         case ResourceKind.Collection:
132             return "Data collection";
133         case ResourceKind.Project:
134             return "Project";
135         case ResourceKind.Process:
136             return "Process";
137         default:
138             return "Unknown";
139     }
140 };
141
142 const renderType = (type: string) => {
143     return <Typography noWrap>
144         {typeToLabel(type)}
145     </Typography>;
146 };
147
148 const renderStatus = (item: ProjectPanelItem) =>
149     <Typography noWrap align="center">
150         {item.status || "-"}
151     </Typography>;
152
153 export enum ProjectPanelColumnNames {
154     Name = "Name",
155     Status = "Status",
156     Type = "Type",
157     Owner = "Owner",
158     FileSize = "File size",
159     LastModified = "Last modified"
160
161 }
162
163 export const columns: DataColumns<ProjectPanelItem, ProjectPanelFilter> = [{
164     name: ProjectPanelColumnNames.Name,
165     selected: true,
166     sortDirection: SortDirection.Asc,
167     render: renderName,
168     width: "450px"
169 }, {
170     name: "Status",
171     selected: true,
172     filters: [{
173         name: ContainerRequestState.Committed,
174         selected: true,
175         type: ContainerRequestState.Committed
176     }, {
177         name: ContainerRequestState.Final,
178         selected: true,
179         type: ContainerRequestState.Final
180     }, {
181         name: ContainerRequestState.Uncommitted,
182         selected: true,
183         type: ContainerRequestState.Uncommitted
184     }],
185     render: renderStatus,
186     width: "75px"
187 }, {
188     name: ProjectPanelColumnNames.Type,
189     selected: true,
190     filters: [{
191         name: typeToLabel(ResourceKind.Collection),
192         selected: true,
193         type: ResourceKind.Collection
194     }, {
195         name: typeToLabel(ResourceKind.Process),
196         selected: true,
197         type: ResourceKind.Process
198     }, {
199         name: typeToLabel(ResourceKind.Project),
200         selected: true,
201         type: ResourceKind.Project
202     }],
203     render: item => renderType(item.kind),
204     width: "125px"
205 }, {
206     name: ProjectPanelColumnNames.Owner,
207     selected: true,
208     render: item => renderOwner(item.owner),
209     width: "200px"
210 }, {
211     name: ProjectPanelColumnNames.FileSize,
212     selected: true,
213     render: item => renderFileSize(item.fileSize),
214     width: "50px"
215 }, {
216     name: ProjectPanelColumnNames.LastModified,
217     selected: true,
218     sortDirection: SortDirection.None,
219     render: item => renderDate(item.lastModified),
220     width: "150px"
221 }];
222
223 const contextMenuActions = [[{
224     icon: "fas fa-users fa-fw",
225     name: "Share"
226 }, {
227     icon: "fas fa-sign-out-alt fa-fw",
228     name: "Move to"
229 }, {
230     icon: "fas fa-star fa-fw",
231     name: "Add to favourite"
232 }, {
233     icon: "fas fa-edit fa-fw",
234     name: "Rename"
235 }, {
236     icon: "fas fa-copy fa-fw",
237     name: "Make a copy"
238 }, {
239     icon: "fas fa-download fa-fw",
240     name: "Download"
241 }], [{
242     icon: "fas fa-trash-alt fa-fw",
243     name: "Remove"
244 }
245 ]];
246
247 export default withStyles(styles)(
248     connect((state: RootState) => ({ currentItemId: state.projects.currentItemId }))(
249         ProjectPanel));