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