Merge branch '18284-vm-listing' into main. Closes #18284
[arvados-workbench2.git] / src / views / public-favorites-panel / public-favorites-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 { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
7 import { DataExplorer } from "views-components/data-explorer/data-explorer";
8 import { connect, DispatchProp } from 'react-redux';
9 import { DataColumns } from 'components/data-table/data-table';
10 import { RouteComponentProps } from 'react-router';
11 import { DataTableFilterItem } from 'components/data-table-filters/data-table-filters';
12 import { SortDirection } from 'components/data-table/data-column';
13 import { ResourceKind } from 'models/resource';
14 import { ArvadosTheme } from 'common/custom-theme';
15 import {
16     ProcessStatus,
17     ResourceFileSize,
18     ResourceLastModifiedDate,
19     ResourceType,
20     ResourceName,
21     ResourceOwnerWithName
22 } from 'views-components/data-explorer/renderers';
23 import { PublicFavoriteIcon } from 'components/icon/icon';
24 import { Dispatch } from 'redux';
25 import {
26     openContextMenu,
27     resourceUuidToContextMenuKind
28 } from 'store/context-menu/context-menu-actions';
29 import { loadDetailsPanel } from 'store/details-panel/details-panel-action';
30 import { navigateTo } from 'store/navigation/navigation-action';
31 import { ContainerRequestState } from "models/container-request";
32 import { RootState } from 'store/store';
33 import { DataTableDefaultView } from 'components/data-table-default-view/data-table-default-view';
34 import { createTree } from 'models/tree';
35 import { getSimpleObjectTypeFilters } from 'store/resource-type-filters/resource-type-filters';
36 import { PUBLIC_FAVORITE_PANEL_ID } from 'store/public-favorites-panel/public-favorites-action';
37 import { PublicFavoritesState } from 'store/public-favorites/public-favorites-reducer';
38 import { getResource, ResourcesState } from 'store/resources/resources';
39 import { GroupContentsResource } from 'services/groups-service/groups-service';
40 import { CollectionResource } from 'models/collection';
41
42 type CssRules = "toolbar" | "button" | "root";
43
44 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
45     toolbar: {
46         paddingBottom: theme.spacing.unit * 3,
47         textAlign: "right"
48     },
49     button: {
50         marginLeft: theme.spacing.unit
51     },
52     root: {
53         width: '100%',
54     },
55 });
56
57 export enum PublicFavoritePanelColumnNames {
58     NAME = "Name",
59     STATUS = "Status",
60     TYPE = "Type",
61     OWNER = "Owner",
62     FILE_SIZE = "File size",
63     LAST_MODIFIED = "Last modified"
64 }
65
66 export interface FavoritePanelFilter extends DataTableFilterItem {
67     type: ResourceKind | ContainerRequestState;
68 }
69
70 export const publicFavoritePanelColumns: DataColumns<string> = [
71     {
72         name: PublicFavoritePanelColumnNames.NAME,
73         selected: true,
74         configurable: true,
75         sortDirection: SortDirection.NONE,
76         filters: createTree(),
77         render: uuid => <ResourceName uuid={uuid} />
78     },
79     {
80         name: "Status",
81         selected: true,
82         configurable: true,
83         filters: createTree(),
84         render: uuid => <ProcessStatus uuid={uuid} />
85     },
86     {
87         name: PublicFavoritePanelColumnNames.TYPE,
88         selected: true,
89         configurable: true,
90         filters: getSimpleObjectTypeFilters(),
91         render: uuid => <ResourceType uuid={uuid} />
92     },
93     {
94         name: PublicFavoritePanelColumnNames.OWNER,
95         selected: false,
96         configurable: true,
97         filters: createTree(),
98         render: uuid => <ResourceOwnerWithName uuid={uuid} />
99     },
100     {
101         name: PublicFavoritePanelColumnNames.FILE_SIZE,
102         selected: true,
103         configurable: true,
104         filters: createTree(),
105         render: uuid => <ResourceFileSize uuid={uuid} />
106     },
107     {
108         name: PublicFavoritePanelColumnNames.LAST_MODIFIED,
109         selected: true,
110         configurable: true,
111         sortDirection: SortDirection.DESC,
112         filters: createTree(),
113         render: uuid => <ResourceLastModifiedDate uuid={uuid} />
114     }
115 ];
116
117 interface PublicFavoritePanelDataProps {
118     publicFavorites: PublicFavoritesState;
119     resources: ResourcesState;
120 }
121
122 interface PublicFavoritePanelActionProps {
123     onItemClick: (item: string) => void;
124     onContextMenu: (resources: ResourcesState) => (event: React.MouseEvent<HTMLElement>, item: string) => void;
125     onDialogOpen: (ownerUuid: string) => void;
126     onItemDoubleClick: (item: string) => void;
127 }
128 const mapStateToProps = ({ publicFavorites, resources }: RootState): PublicFavoritePanelDataProps => ({
129     publicFavorites,
130     resources,
131 });
132
133 const mapDispatchToProps = (dispatch: Dispatch): PublicFavoritePanelActionProps => ({
134     onContextMenu: (resources: ResourcesState) => (event, resourceUuid) => {
135         const resource = getResource<GroupContentsResource>(resourceUuid)(resources);
136         const kind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
137         if (kind && resource) {
138             dispatch<any>(openContextMenu(event, {
139                 name: resource.name,
140                 description: resource.description,
141                 storageClassesDesired: (resource as CollectionResource).storageClassesDesired,
142                 uuid: resourceUuid,
143                 ownerUuid: '',
144                 kind: ResourceKind.NONE,
145                 menuKind: kind
146             }));
147         }
148         dispatch<any>(loadDetailsPanel(resourceUuid));
149     },
150     onDialogOpen: (ownerUuid: string) => { return; },
151     onItemClick: (uuid: string) => {
152         dispatch<any>(loadDetailsPanel(uuid));
153     },
154     onItemDoubleClick: uuid => {
155         dispatch<any>(navigateTo(uuid));
156     }
157 });
158
159 type FavoritePanelProps = PublicFavoritePanelDataProps & PublicFavoritePanelActionProps & DispatchProp
160     & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
161
162 export const PublicFavoritePanel = withStyles(styles)(
163     connect(mapStateToProps, mapDispatchToProps)(
164         class extends React.Component<FavoritePanelProps> {
165             render() {
166                 return <div className={this.props.classes.root}><DataExplorer
167                     id={PUBLIC_FAVORITE_PANEL_ID}
168                     onRowClick={this.props.onItemClick}
169                     onRowDoubleClick={this.props.onItemDoubleClick}
170                     onContextMenu={this.props.onContextMenu(this.props.resources)}
171                     contextMenuColumn={true}
172                     dataTableDefaultView={
173                         <DataTableDefaultView
174                             icon={PublicFavoriteIcon}
175                             messages={['Public favorites list is empty.']} />
176                     } /></div>;
177             }
178         }
179     )
180 );