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