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