Merge branch '21225-project-panel-tabs' into main. Closes #21225
[arvados.git] / services / workbench2 / src / views / collection-content-address-panel / collection-content-address-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 {
7     StyleRulesCallback,
8     WithStyles,
9     withStyles,
10     Button
11 } from '@material-ui/core';
12 import { CollectionIcon } from 'components/icon/icon';
13 import { ArvadosTheme } from 'common/custom-theme';
14 import { BackIcon } from 'components/icon/icon';
15 import { COLLECTIONS_CONTENT_ADDRESS_PANEL_ID } from 'store/collections-content-address-panel/collections-content-address-panel-actions';
16 import { DataExplorer } from "views-components/data-explorer/data-explorer";
17 import { Dispatch } from 'redux';
18 import {
19     resourceUuidToContextMenuKind,
20     openContextMenu
21 } from 'store/context-menu/context-menu-actions';
22 import { ResourceKind } from 'models/resource';
23 import { loadDetailsPanel } from 'store/details-panel/details-panel-action';
24 import { connect } from 'react-redux';
25 import { navigateTo } from 'store/navigation/navigation-action';
26 import { DataColumns } from 'components/data-table/data-table';
27 import { SortDirection } from 'components/data-table/data-column';
28 import { createTree } from 'models/tree';
29 import {
30     ResourceName,
31     ResourceOwnerName,
32     ResourceLastModifiedDate,
33     ResourceStatus
34 } from 'views-components/data-explorer/renderers';
35 import { getResource, ResourcesState } from 'store/resources/resources';
36 import { RootState } from 'store/store';
37 import { CollectionResource } from 'models/collection';
38
39 type CssRules = 'backLink' | 'backIcon' | 'root' | 'content';
40
41 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
42     backLink: {
43         fontSize: '12px',
44         fontWeight: 600,
45         display: 'flex',
46         alignItems: 'center',
47         padding: theme.spacing.unit,
48         marginBottom: theme.spacing.unit,
49         color: theme.palette.grey["500"],
50     },
51     backIcon: {
52         marginRight: theme.spacing.unit
53     },
54     root: {
55         width: '100%',
56     },
57     content: {
58         // reserve space for the content address bar
59         height: `calc(100% - ${theme.spacing.unit * 7}px)`,
60     },
61 });
62
63 enum CollectionContentAddressPanelColumnNames {
64     COLLECTION_WITH_THIS_ADDRESS = "Collection with this address",
65     STATUS = "Status",
66     LOCATION = "Location",
67     LAST_MODIFIED = "Last modified"
68 }
69
70 export const collectionContentAddressPanelColumns: DataColumns<string, CollectionResource> = [
71     {
72         name: CollectionContentAddressPanelColumnNames.COLLECTION_WITH_THIS_ADDRESS,
73         selected: true,
74         configurable: true,
75         sort: {direction: SortDirection.NONE, field: "uuid"},
76         filters: createTree(),
77         render: uuid => <ResourceName uuid={uuid} />
78     },
79     {
80         name: CollectionContentAddressPanelColumnNames.STATUS,
81         selected: true,
82         configurable: true,
83         filters: createTree(),
84         render: uuid => <ResourceStatus uuid={uuid} />
85     },
86     {
87         name: CollectionContentAddressPanelColumnNames.LOCATION,
88         selected: true,
89         configurable: true,
90         filters: createTree(),
91         render: uuid => <ResourceOwnerName uuid={uuid} />
92     },
93     {
94         name: CollectionContentAddressPanelColumnNames.LAST_MODIFIED,
95         selected: true,
96         configurable: true,
97         sort: {direction: SortDirection.DESC, field: "modifiedAt"},
98         filters: createTree(),
99         render: uuid => <ResourceLastModifiedDate uuid={uuid} />
100     }
101 ];
102
103 interface CollectionContentAddressPanelActionProps {
104     onContextMenu: (resources: ResourcesState) => (event: React.MouseEvent<any>, uuid: string) => void;
105     onItemClick: (item: string) => void;
106     onItemDoubleClick: (item: string) => void;
107 }
108
109 interface CollectionContentAddressPanelDataProps {
110     resources: ResourcesState;
111 }
112
113 const mapStateToProps = ({ resources }: RootState): CollectionContentAddressPanelDataProps => ({
114     resources
115 })
116
117 const mapDispatchToProps = (dispatch: Dispatch): CollectionContentAddressPanelActionProps => ({
118     onContextMenu: (resources: ResourcesState) => (event, resourceUuid) => {
119         const resource = getResource<CollectionResource>(resourceUuid)(resources);
120         const kind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
121         if (kind) {
122             dispatch<any>(openContextMenu(event, {
123                 name: resource ? resource.name : '',
124                 description: resource ? resource.description : '',
125                 storageClassesDesired: resource ? resource.storageClassesDesired : [],
126                 uuid: resourceUuid,
127                 ownerUuid: '',
128                 kind: ResourceKind.NONE,
129                 menuKind: kind
130             }));
131         }
132         dispatch<any>(loadDetailsPanel(resourceUuid));
133     },
134     onItemClick: (uuid: string) => {
135         dispatch<any>(loadDetailsPanel(uuid));
136     },
137     onItemDoubleClick: uuid => {
138         dispatch<any>(navigateTo(uuid));
139     }
140 });
141
142 interface CollectionContentAddressDataProps {
143     match: {
144         params: { id: string }
145     };
146 }
147
148 export const CollectionsContentAddressPanel = withStyles(styles)(
149     connect(mapStateToProps, mapDispatchToProps)(
150         class extends React.Component<CollectionContentAddressPanelActionProps & CollectionContentAddressPanelDataProps & CollectionContentAddressDataProps & WithStyles<CssRules>> {
151             render() {
152                 return <div className={this.props.classes.root}>
153                     <Button
154                         onClick={() => window.history.back()}
155                         className={this.props.classes.backLink}>
156                         <BackIcon className={this.props.classes.backIcon} />
157                         Back
158                     </Button>
159                     <div className={this.props.classes.content}><DataExplorer
160                         id={COLLECTIONS_CONTENT_ADDRESS_PANEL_ID}
161                         hideSearchInput
162                         onRowClick={this.props.onItemClick}
163                         onRowDoubleClick={this.props.onItemDoubleClick}
164                         onContextMenu={this.props.onContextMenu(this.props.resources)}
165                         contextMenuColumn={true}
166                         title={`Content address: ${this.props.match.params.id}`}
167                         defaultViewIcon={CollectionIcon}
168                         defaultViewMessages={['Collections with this content address not found.']}
169                         forceMultiSelectMode />
170                     </div>
171                 </div>;
172             }
173         }
174     )
175 );