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