7ea0856dd4965cab1214506832aa7bc6c9031589
[arvados-workbench2.git] / src / components / data-explorer / data-explorer.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 { DataTable, DataTableProps, Column, ColumnsConfigurator } from "../../components/data-table";
7 import { Typography, Grid, ListItem, Divider, List, ListItemIcon, ListItemText } from '@material-ui/core';
8 import IconButton, { IconButtonProps } from '@material-ui/core/IconButton';
9 import MoreVertIcon from "@material-ui/icons/MoreVert";
10 import Popover from '../popover/popover';
11
12 export interface DataItem {
13     name: string;
14     type: string;
15     owner: string;
16     lastModified: string;
17     fileSize?: number;
18     status?: string;
19 }
20
21 interface DataExplorerProps {
22     items: DataItem[];
23     onItemClick: (item: DataItem) => void;
24 }
25
26 type DataExplorerState = Pick<DataTableProps<DataItem>, "columns">;
27
28 class DataExplorer extends React.Component<DataExplorerProps, DataExplorerState> {
29
30     state: DataExplorerState = {
31         columns: [
32             {
33                 header: "Name",
34                 selected: true,
35                 render: item => (
36                     <Grid
37                         container
38                         alignItems="center"
39                         wrap="nowrap"
40                         spacing={16}
41                         onClick={() => this.props.onItemClick(item)}
42                     >
43                         <Grid item>
44                             {renderIcon(item)}
45                         </Grid>
46                         <Grid item>
47                             <Typography color="primary">
48                                 {item.name}
49                             </Typography>
50                         </Grid>
51                     </Grid>
52                 )
53             },
54             {
55                 header: "Status",
56                 selected: true,
57                 render: item => (
58                     <Typography noWrap align="center">
59                         {item.status || "-"}
60                     </Typography>
61                 )
62             },
63             {
64                 header: "Type",
65                 selected: true,
66                 render: item => (
67                     <Typography noWrap>
68                         {item.type}
69                     </Typography>
70                 )
71             },
72             {
73                 header: "Owner",
74                 selected: true,
75                 render: item => (
76                     <Typography noWrap color="primary">
77                         {item.owner}
78                     </Typography>
79                 )
80             },
81             {
82                 header: "File size",
83                 selected: true,
84                 render: ({ fileSize }) => (
85                     <Typography noWrap>
86                         {typeof fileSize === "number" ? formatFileSize(fileSize) : "-"}
87                     </Typography>
88                 )
89             },
90             {
91                 header: "Last modified",
92                 selected: true,
93                 render: item => (
94                     <Typography noWrap>
95                         {formatDate(item.lastModified)}
96                     </Typography>
97                 )
98             },
99             {
100                 header: "Actions",
101                 key: "Actions",
102                 selected: true,
103                 configurable: false,
104                 renderHeader: () => (
105                     <Grid container justify="flex-end">
106                         <ColumnsConfigurator
107                             columns={this.state.columns}
108                             onColumnToggle={this.toggleColumn}
109                         />
110                     </Grid>
111                 ),
112                 render: item => (
113                     <Grid container justify="flex-end">
114                         <Popover triggerComponent={ItemActionsTrigger}>
115                             <List dense>
116                                 {[
117                                     {
118                                         icon: "fas fa-users",
119                                         label: "Share"
120                                     },
121                                     {
122                                         icon: "fas fa-sign-out-alt",
123                                         label: "Move to"
124                                     },
125                                     {
126                                         icon: "fas fa-star",
127                                         label: "Add to favourite"
128                                     },
129                                     {
130                                         icon: "fas fa-edit",
131                                         label: "Rename"
132                                     },
133                                     {
134                                         icon: "fas fa-copy",
135                                         label: "Make a copy"
136                                     },
137                                     {
138                                         icon: "fas fa-download",
139                                         label: "Download"
140                                     }].map(renderAction)
141                                 }
142                                 < Divider />
143                                 {
144                                     renderAction({ icon: "fas fa-trash-alt", label: "Remove" })
145                                 }
146                             </List>
147                         </Popover>
148                     </Grid>
149                 )
150             }
151         ]
152     };
153
154     render() {
155         return (
156             <DataTable
157                 columns={this.state.columns}
158                 items={this.props.items}
159                 onColumnToggle={this.toggleColumn}
160             />
161         );
162     }
163
164     toggleColumn = (column: Column<DataItem>) => {
165         const index = this.state.columns.indexOf(column);
166         const columns = this.state.columns.slice(0);
167         columns.splice(index, 1, { ...column, selected: !column.selected });
168         this.setState({ columns });
169     }
170 }
171
172 const formatDate = (isoDate: string) => {
173     const date = new Date(isoDate);
174     return date.toLocaleString();
175 };
176
177 const formatFileSize = (size: number) => {
178     switch (true) {
179         case size > 1000000000000:
180             return `${size / 1000000000000} TB`;
181         case size > 1000000000:
182             return `${size / 1000000000} GB`;
183         case size > 1000000:
184             return `${size / 1000000} MB`;
185         case size > 1000:
186             return `${size / 1000} KB`;
187         default:
188             return `${size} B`;
189     }
190 };
191
192 const renderIcon = (DataItem: DataItem) => {
193     switch (DataItem.type) {
194         case "arvados#group":
195             return <i className="fas fa-folder fa-lg" />;
196         case "arvados#groupList":
197             return <i className="fas fa-th fa-lg" />;
198         default:
199             return <i />;
200     }
201 };
202
203 const renderAction = (action: { label: string, icon: string }, index?: number) => (
204     <ListItem button key={index}>
205         <ListItemIcon>
206             <i className={action.icon} />
207         </ListItemIcon>
208         <ListItemText>
209             {action.label}
210         </ListItemText>
211     </ListItem>
212 );
213
214 const ItemActionsTrigger: React.SFC<IconButtonProps> = (props) => (
215     <IconButton {...props}>
216         <MoreVertIcon />
217     </IconButton>
218 );
219
220 export default DataExplorer;