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