fileCount column up Arvados-DCO-1.1-Signed-off-by: Lisa Knox <lisa.knox@curii.com>
[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 React from 'react';
6 import withStyles from "@material-ui/core/styles/withStyles";
7 import { DispatchProp, connect } from 'react-redux';
8 import { RouteComponentProps } from 'react-router';
9 import { StyleRulesCallback, WithStyles } from "@material-ui/core";
10
11 import { DataExplorer } from "views-components/data-explorer/data-explorer";
12 import { DataColumns } from 'components/data-table/data-table';
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, Resource } from 'models/resource';
18 import {
19     ResourceFileSize,
20     ResourceFileCount,
21     ResourceCreatedAtDate,
22     ResourceLastModifiedDate,
23     ResourceTrashDate,
24     ResourceDeleteDate,
25     ProcessStatus,
26     ResourceType,
27     ResourceUUID,
28     ResourceMetadata,
29     ResourceVersion,
30     ResourceDescription,
31     ResourceOwnerWithName
32 } from 'views-components/data-explorer/renderers';
33 import { ProjectIcon } from 'components/icon/icon';
34 import { ResourceName } from 'views-components/data-explorer/renderers';
35 import {
36     ResourcesState,
37     getResource
38 } from 'store/resources/resources';
39 import { loadDetailsPanel } from 'store/details-panel/details-panel-action';
40 import {
41     openContextMenu,
42     resourceUuidToContextMenuKind
43 } from 'store/context-menu/context-menu-actions';
44 import { navigateTo } from 'store/navigation/navigation-action';
45 import { getProperty } from 'store/properties/properties';
46 import { PROJECT_PANEL_CURRENT_UUID } from 'store/project-panel/project-panel-action';
47 import { ArvadosTheme } from "common/custom-theme";
48 import { createTree } from 'models/tree';
49 import {
50     getInitialResourceTypeFilters,
51     getInitialProcessStatusFilters
52 } from 'store/resource-type-filters/resource-type-filters';
53 import { GroupContentsResource } from 'services/groups-service/groups-service';
54 import { GroupClass, GroupResource } from 'models/group';
55 import { CollectionResource } from 'models/collection';
56 import { resourceIsFrozen } from 'common/frozen-resources';
57
58 type CssRules = 'root' | "button";
59
60 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
61     root: {
62         width: '100%',
63     },
64     button: {
65         marginLeft: theme.spacing.unit
66     },
67 });
68
69 export enum ProjectPanelColumnNames {
70     NAME = "Name",
71     STATUS = "Status",
72     TYPE = "Type",
73     OWNER = "Owner",
74     FILE_SIZE = "File size",
75     FILE_COUNT = "File count",
76     UUID = "UUID",
77     CREATED_AT = "Date created",
78     LAST_MODIFIED = "Last modified",
79     TRASH_AT = "Trash at",
80     DELETE_AT = "Delete at",
81     DESCRIPTION = "Description",
82     METADATA = "Metadata",
83     VERSION = "Version"
84 }
85
86 export interface ProjectPanelFilter extends DataTableFilterItem {
87     type: ResourceKind | ContainerRequestState;
88 }
89
90 export const projectPanelColumns: DataColumns<string> = [
91     {
92         name: ProjectPanelColumnNames.NAME,
93         selected: true,
94         configurable: true,
95         sortDirection: SortDirection.NONE,
96         filters: createTree(),
97         render: uuid => <ResourceName uuid={uuid} />
98     },
99     {
100         name: "Status",
101         selected: true,
102         configurable: true,
103         mutuallyExclusiveFilters: true,
104         filters: getInitialProcessStatusFilters(),
105         render: uuid => <ProcessStatus uuid={uuid} />,
106     },
107     {
108         name: ProjectPanelColumnNames.TYPE,
109         selected: true,
110         configurable: true,
111         filters: getInitialResourceTypeFilters(),
112         render: uuid => <ResourceType uuid={uuid} />
113     },
114     {
115         name: ProjectPanelColumnNames.OWNER,
116         selected: false,
117         configurable: true,
118         filters: createTree(),
119         render: uuid => <ResourceOwnerWithName uuid={uuid} />
120     },
121     {
122         name: ProjectPanelColumnNames.FILE_SIZE,
123         selected: true,
124         configurable: true,
125         filters: createTree(),
126         render: uuid => <ResourceFileSize uuid={uuid} />
127     },
128     {
129         name: ProjectPanelColumnNames.FILE_COUNT,
130         selected: true,
131         configurable: true,
132         filters: createTree(),
133         render: uuid =><ResourceFileCount uuid={uuid}/>
134     },
135     {
136         name: ProjectPanelColumnNames.UUID,
137         selected: true,
138         configurable: true,
139         filters: createTree(),
140         render: uuid => <ResourceUUID uuid={uuid}/>
141     },
142     {
143         name: ProjectPanelColumnNames.METADATA,
144         selected: false,
145         configurable: true,
146         filters: createTree(),
147         render: uuid => <ResourceMetadata uuid={uuid}/>
148     },
149     {
150         name: ProjectPanelColumnNames.CREATED_AT,
151         selected: false,
152         configurable: true,
153         filters: createTree(),
154         render: uuid =><ResourceCreatedAtDate uuid={uuid}/>
155     },
156     {
157         name: ProjectPanelColumnNames.LAST_MODIFIED,
158         selected: false,
159         configurable: true,
160         sortDirection: SortDirection.DESC,
161         filters: createTree(),
162         render: uuid => <ResourceLastModifiedDate uuid={uuid} />
163     },
164     {
165         name: ProjectPanelColumnNames.TRASH_AT,
166         selected: false,
167         configurable: true,
168         sortDirection: SortDirection.DESC,
169         filters: createTree(),
170         render: uuid => <ResourceTrashDate uuid={uuid} />
171     },
172     {
173         name: ProjectPanelColumnNames.DELETE_AT,
174         selected: false,
175         configurable: true,
176         sortDirection: SortDirection.DESC,
177         filters: createTree(),
178         render: uuid => <ResourceDeleteDate uuid={uuid} />
179     },
180     {
181         name: ProjectPanelColumnNames.DESCRIPTION,
182         selected: true,
183         configurable: true,
184         filters: createTree(),
185         render: uuid =><ResourceDescription uuid={uuid}/>
186     },
187     {
188         name: ProjectPanelColumnNames.VERSION,
189         selected: false,
190         configurable: true,
191         filters: createTree(),
192         render: uuid =><ResourceVersion uuid={uuid}/>
193     }
194     
195 ];
196
197 export const PROJECT_PANEL_ID = "projectPanel";
198
199 const DEFAULT_VIEW_MESSAGES = [
200     'Your project is empty.',
201     'Please create a project or create a collection and upload a data.',
202 ];
203
204 interface ProjectPanelDataProps {
205     currentItemId: string;
206     resources: ResourcesState;
207     isAdmin: boolean;
208     userUuid: string;
209     dataExplorerItems: any;
210 }
211
212 type ProjectPanelProps = ProjectPanelDataProps & DispatchProp
213     & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
214
215
216 export const ProjectPanel = withStyles(styles)(
217     connect((state: RootState) => ({
218         currentItemId: getProperty(PROJECT_PANEL_CURRENT_UUID)(state.properties),
219         resources: state.resources,
220         userUuid: state.auth.user!.uuid
221     }))(
222         class extends React.Component<ProjectPanelProps> {
223             render() {
224                 const { classes } = this.props;
225
226                 return <div data-cy='project-panel' className={classes.root}>
227                     <DataExplorer
228                         id={PROJECT_PANEL_ID}
229                         onRowClick={this.handleRowClick}
230                         onRowDoubleClick={this.handleRowDoubleClick}
231                         onContextMenu={this.handleContextMenu}
232                         contextMenuColumn={true}
233                         defaultViewIcon={ProjectIcon}
234                         defaultViewMessages={DEFAULT_VIEW_MESSAGES}
235                     />
236                 </div>;
237             }
238
239             isCurrentItemChild = (resource: Resource) => {
240                 return resource.ownerUuid === this.props.currentItemId;
241             }
242
243             handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
244                 const { resources, isAdmin } = this.props;
245                 const resource = getResource<GroupContentsResource>(resourceUuid)(resources);
246                 // When viewing the contents of a filter group, all contents should be treated as read only.
247                 let readonly = false;
248                 const project = getResource<GroupResource>(this.props.currentItemId)(resources);
249                 if (project && project.groupClass === GroupClass.FILTER) {
250                     readonly = true;
251                 }
252
253                 const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid, readonly));
254                 if (menuKind && resource) {
255                     this.props.dispatch<any>(openContextMenu(event, {
256                         name: resource.name,
257                         uuid: resource.uuid,
258                         ownerUuid: resource.ownerUuid,
259                         isTrashed: ('isTrashed' in resource) ? resource.isTrashed : false,
260                         kind: resource.kind,
261                         menuKind,
262                         isAdmin,
263                         isFrozen: resourceIsFrozen(resource, resources),
264                         description: resource.description,
265                         storageClassesDesired: (resource as CollectionResource).storageClassesDesired,
266                         properties: ('properties' in resource) ? resource.properties : {},
267                     }));
268                 }
269                 this.props.dispatch<any>(loadDetailsPanel(resourceUuid));
270             }
271
272             handleRowDoubleClick = (uuid: string) => {
273                 this.props.dispatch<any>(navigateTo(uuid));
274             }
275
276             handleRowClick = (uuid: string) => {
277                 this.props.dispatch<any>(loadDetailsPanel(uuid));
278             }
279
280         }
281     )
282 );