Merge branch '18123-group-edit-page-rebase1' into main. Closes #18123
[arvados-workbench2.git] / src / views / group-details-panel / group-details-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 { connect } from 'react-redux';
7
8 import { DataExplorer } from "views-components/data-explorer/data-explorer";
9 import { DataColumns } from 'components/data-table/data-table';
10 import { ResourceLinkHeadUuid, ResourceLinkTailUsername, ResourceLinkHeadPermissionLevel, ResourceLinkTailPermissionLevel, ResourceLinkHead, ResourceLinkTail, ResourceLinkDelete, ResourceLinkTailIsActive, ResourceLinkTailIsVisible } from 'views-components/data-explorer/renderers';
11 import { createTree } from 'models/tree';
12 import { noop } from 'lodash/fp';
13 import { RootState } from 'store/store';
14 import { GROUP_DETAILS_MEMBERS_PANEL_ID, GROUP_DETAILS_PERMISSIONS_PANEL_ID, openAddGroupMembersDialog, getCurrentGroupDetailsPanelUuid } from 'store/group-details-panel/group-details-panel-actions';
15 import { openContextMenu } from 'store/context-menu/context-menu-actions';
16 import { ResourcesState, getResource } from 'store/resources/resources';
17 import { Grid, Button, Tabs, Tab, Paper, WithStyles, withStyles, StyleRulesCallback } from '@material-ui/core';
18 import { AddIcon } from 'components/icon/icon';
19 import { getUserUuid } from 'common/getuser';
20 import { GroupResource, isBuiltinGroup } from 'models/group';
21 import { ArvadosTheme } from 'common/custom-theme';
22
23 type CssRules = "root";
24
25 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
26     root: {
27         width: '100%',
28     }
29 });
30
31 export enum GroupDetailsPanelMembersColumnNames {
32     FULL_NAME = "Name",
33     USERNAME = "Username",
34     ACTIVE = "User Active",
35     VISIBLE = "Visible to other members",
36     PERMISSION = "Permission",
37     REMOVE = "Remove",
38 }
39
40 export enum GroupDetailsPanelPermissionsColumnNames {
41     NAME = "Name",
42     PERMISSION = "Permission",
43     UUID = "UUID",
44     REMOVE = "Remove",
45 }
46
47 export const groupDetailsMembersPanelColumns: DataColumns<string> = [
48     {
49         name: GroupDetailsPanelMembersColumnNames.FULL_NAME,
50         selected: true,
51         configurable: true,
52         filters: createTree(),
53         render: uuid => <ResourceLinkTail uuid={uuid} />
54     },
55     {
56         name: GroupDetailsPanelMembersColumnNames.USERNAME,
57         selected: true,
58         configurable: true,
59         filters: createTree(),
60         render: uuid => <ResourceLinkTailUsername uuid={uuid} />
61     },
62     {
63         name: GroupDetailsPanelMembersColumnNames.ACTIVE,
64         selected: true,
65         configurable: true,
66         filters: createTree(),
67         render: uuid => <ResourceLinkTailIsActive uuid={uuid} disabled={true} />
68     },
69     {
70         name: GroupDetailsPanelMembersColumnNames.VISIBLE,
71         selected: true,
72         configurable: true,
73         filters: createTree(),
74         render: uuid => <ResourceLinkTailIsVisible uuid={uuid} />
75     },
76     {
77         name: GroupDetailsPanelMembersColumnNames.PERMISSION,
78         selected: true,
79         configurable: true,
80         filters: createTree(),
81         render: uuid => <ResourceLinkTailPermissionLevel uuid={uuid} />
82     },
83     {
84         name: GroupDetailsPanelMembersColumnNames.REMOVE,
85         selected: true,
86         configurable: true,
87         filters: createTree(),
88         render: uuid => <ResourceLinkDelete uuid={uuid} />
89     },
90 ];
91
92 export const groupDetailsPermissionsPanelColumns: DataColumns<string> = [
93     {
94         name: GroupDetailsPanelPermissionsColumnNames.NAME,
95         selected: true,
96         configurable: true,
97         filters: createTree(),
98         render: uuid => <ResourceLinkHead uuid={uuid} />
99     },
100     {
101         name: GroupDetailsPanelPermissionsColumnNames.PERMISSION,
102         selected: true,
103         configurable: true,
104         filters: createTree(),
105         render: uuid => <ResourceLinkHeadPermissionLevel uuid={uuid} />
106     },
107     {
108         name: GroupDetailsPanelPermissionsColumnNames.UUID,
109         selected: true,
110         configurable: true,
111         filters: createTree(),
112         render: uuid => <ResourceLinkHeadUuid uuid={uuid} />
113     },
114     {
115         name: GroupDetailsPanelPermissionsColumnNames.REMOVE,
116         selected: true,
117         configurable: true,
118         filters: createTree(),
119         render: uuid => <ResourceLinkDelete uuid={uuid} />
120     },
121 ];
122
123 const mapStateToProps = (state: RootState) => {
124     const groupUuid = getCurrentGroupDetailsPanelUuid(state.properties);
125     const group = getResource<GroupResource>(groupUuid || '')(state.resources);
126     const userUuid = getUserUuid(state);
127
128     return {
129         resources: state.resources,
130         groupCanManage: userUuid && !isBuiltinGroup(group?.uuid || '')
131                             ? group?.writableBy?.includes(userUuid)
132                             : false,
133     };
134 };
135
136 const mapDispatchToProps = {
137     onContextMenu: openContextMenu,
138     onAddUser: openAddGroupMembersDialog,
139 };
140
141 export interface GroupDetailsPanelProps {
142     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: any) => void;
143     onAddUser: () => void;
144     resources: ResourcesState;
145     groupCanManage: boolean;
146 }
147
148 export const GroupDetailsPanel = withStyles(styles)(connect(
149     mapStateToProps, mapDispatchToProps
150 )(
151     class GroupDetailsPanel extends React.Component<GroupDetailsPanelProps & WithStyles<CssRules>> {
152         state = {
153           value: 0,
154         };
155
156         componentDidMount() {
157             this.setState({ value: 0 });
158         }
159
160         render() {
161             const { value } = this.state;
162             return (
163                 <Paper className={this.props.classes.root}>
164                   <Tabs value={value} onChange={this.handleChange} variant="fullWidth">
165                       <Tab data-cy="group-details-members-tab" label="MEMBERS" />
166                       <Tab data-cy="group-details-permissions-tab" label="PERMISSIONS" />
167                   </Tabs>
168                   {value === 0 &&
169                       <DataExplorer
170                           id={GROUP_DETAILS_MEMBERS_PANEL_ID}
171                           data-cy="group-members-data-explorer"
172                           onRowClick={noop}
173                           onRowDoubleClick={noop}
174                           onContextMenu={noop}
175                           contextMenuColumn={false}
176                           hideColumnSelector
177                           hideSearchInput
178                           actions={
179                                 this.props.groupCanManage &&
180                                 <Grid container justify='flex-end'>
181                                     <Button
182                                       data-cy="group-member-add"
183                                       variant="contained"
184                                       color="primary"
185                                       onClick={this.props.onAddUser}>
186                                       <AddIcon /> Add user
187                                     </Button>
188                                 </Grid>
189                           }
190                           paperProps={{
191                               elevation: 0,
192                           }} />
193                   }
194                   {value === 1 &&
195                       <DataExplorer
196                           id={GROUP_DETAILS_PERMISSIONS_PANEL_ID}
197                           data-cy="group-permissions-data-explorer"
198                           onRowClick={noop}
199                           onRowDoubleClick={noop}
200                           onContextMenu={noop}
201                           contextMenuColumn={false}
202                           hideColumnSelector
203                           hideSearchInput
204                           paperProps={{
205                               elevation: 0,
206                           }} />
207                   }
208                 </Paper>
209             );
210         }
211
212         handleChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
213             this.setState({ value });
214         }
215     }));