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