c0c80e3cdffc369f2166043d526fd0140c06997b
[arvados-workbench2.git] / src / views / user-profile-panel / user-profile-panel-root.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 { Field, InjectedFormProps } from "redux-form";
7 import { TextField } from "components/text-field/text-field";
8 import { DataExplorer } from "views-components/data-explorer/data-explorer";
9 import { NativeSelectField } from "components/select-field/select-field";
10 import {
11     StyleRulesCallback,
12     WithStyles,
13     withStyles,
14     Card,
15     CardContent,
16     Button,
17     Typography,
18     Grid,
19     InputLabel,
20     Tabs, Tab,
21     Paper
22 } from '@material-ui/core';
23 import { ArvadosTheme } from 'common/custom-theme';
24 import { User } from "models/user";
25 import { DataTableDefaultView } from 'components/data-table-default-view/data-table-default-view';
26 import { MY_ACCOUNT_VALIDATION } from "validators/validators";
27 import { USER_PROFILE_PANEL_ID } from 'store/user-profile/user-profile-actions';
28 import { noop } from 'lodash';
29 import { GroupsIcon } from 'components/icon/icon';
30 import { DataColumns } from 'components/data-table/data-table';
31 import { ResourceLinkHeadUuid, ResourceLinkHeadPermissionLevel, ResourceLinkHead, ResourceLinkDelete, ResourceLinkTailIsVisible } from 'views-components/data-explorer/renderers';
32 import { createTree } from 'models/tree';
33
34 type CssRules = 'root' | 'adminRoot' | 'gridItem' | 'label' | 'title' | 'description' | 'actions' | 'content';
35
36 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
37     root: {
38         width: '100%',
39         overflow: 'auto'
40     },
41     adminRoot: {
42         // ...theme.mixins.gutters()
43     },
44     gridItem: {
45         height: 45,
46         marginBottom: 20
47     },
48     label: {
49         fontSize: '0.675rem'
50     },
51     title: {
52         fontSize: '1.1rem',
53     },
54     description: {
55         color: theme.palette.grey["600"]
56     },
57     actions: {
58         display: 'flex',
59         justifyContent: 'flex-end'
60     },
61     content: {
62         // reserve space for the tab bar
63         height: `calc(100% - ${theme.spacing.unit * 7}px)`,
64     }
65 });
66
67 export interface UserProfilePanelRootActionProps {
68     openSetupShellAccount: (uuid: string) => void;
69     loginAs: (uuid: string) => void;
70     openDeactivateDialog: (uuid: string) => void;
71 }
72
73 export interface UserProfilePanelRootDataProps {
74     isPristine: boolean;
75     isValid: boolean;
76     initialValues?: User;
77     localCluster: string;
78 }
79
80 const RoleTypes = [
81     { key: 'Bio-informatician', value: 'Bio-informatician' },
82     { key: 'Data Scientist', value: 'Data Scientist' },
83     { key: 'Analyst', value: 'Analyst' },
84     { key: 'Researcher', value: 'Researcher' },
85     { key: 'Software Developer', value: 'Software Developer' },
86     { key: 'System Administrator', value: 'System Administrator' },
87     { key: 'Other', value: 'Other' }
88 ];
89
90 type UserProfilePanelRootProps = InjectedFormProps<{}> & UserProfilePanelRootActionProps & UserProfilePanelRootDataProps & WithStyles<CssRules>;
91
92 // type LocalClusterProp = { localCluster: string };
93 // const renderField: React.ComponentType<WrappedFieldProps & LocalClusterProp> = ({ input, localCluster }) => (
94 //     <span>{localCluster === input.value.substring(0, 5) ? "" : "federated"} user {input.value}</span>
95 // );
96
97 export enum UserProfileGroupsColumnNames {
98     NAME = "Name",
99     PERMISSION = "Permission",
100     VISIBLE = "Visible to other members",
101     UUID = "UUID",
102     REMOVE = "Remove",
103 }
104
105 export const userProfileGroupsColumns: DataColumns<string> = [
106     {
107         name: UserProfileGroupsColumnNames.NAME,
108         selected: true,
109         configurable: true,
110         filters: createTree(),
111         render: uuid => <ResourceLinkHead uuid={uuid} />
112     },
113     {
114         name: UserProfileGroupsColumnNames.PERMISSION,
115         selected: true,
116         configurable: true,
117         filters: createTree(),
118         render: uuid => <ResourceLinkHeadPermissionLevel uuid={uuid} />
119     },
120     {
121         name: UserProfileGroupsColumnNames.VISIBLE,
122         selected: true,
123         configurable: true,
124         filters: createTree(),
125         render: uuid => <ResourceLinkTailIsVisible uuid={uuid} />
126     },
127     {
128         name: UserProfileGroupsColumnNames.UUID,
129         selected: true,
130         configurable: true,
131         filters: createTree(),
132         render: uuid => <ResourceLinkHeadUuid uuid={uuid} />
133     },
134     {
135         name: UserProfileGroupsColumnNames.REMOVE,
136         selected: true,
137         configurable: true,
138         filters: createTree(),
139         render: uuid => <ResourceLinkDelete uuid={uuid} />
140     },
141 ];
142
143 export const UserProfilePanelRoot = withStyles(styles)(
144     class extends React.Component<UserProfilePanelRootProps> {
145         state = {
146             value: 0,
147         };
148
149         componentDidMount() {
150             this.setState({ value: 0 });
151         }
152
153         render() {
154             return <Paper className={this.props.classes.root}>
155                 {/* <Typography variant="title" className={this.props.classes.title}>
156                     Logged in as <Field name="uuid" component={renderField} localCluster={this.props.localCluster} />
157                 </Typography> */}
158                 <Tabs value={this.state.value} onChange={this.handleChange} fullWidth>
159                     <Tab label="PROFILE" />
160                     <Tab label="GROUPS" />
161                     <Tab label="ADMIN" />
162                 </Tabs>
163                 {this.state.value === 0 &&
164                     // <Card className={this.props.classes.root}>
165                         <CardContent>
166                             <form onSubmit={this.props.handleSubmit}>
167                                 <Grid container spacing={24}>
168                                     <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
169                                         <Field
170                                             label="First name"
171                                             name="firstName"
172                                             component={TextField as any}
173                                             disabled
174                                         />
175                                     </Grid>
176                                     <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
177                                         <Field
178                                             label="Last name"
179                                             name="lastName"
180                                             component={TextField as any}
181                                             disabled
182                                         />
183                                     </Grid>
184                                     <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
185                                         <Field
186                                             label="E-mail"
187                                             name="email"
188                                             component={TextField as any}
189                                             disabled
190                                         />
191                                     </Grid>
192                                     <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
193                                         <Field
194                                             label="Username"
195                                             name="username"
196                                             component={TextField as any}
197                                             disabled
198                                         />
199                                     </Grid>
200                                     <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
201                                         <Field
202                                             label="Organization"
203                                             name="prefs.profile.organization"
204                                             component={TextField as any}
205                                             validate={MY_ACCOUNT_VALIDATION}
206                                             required
207                                         />
208                                     </Grid>
209                                     <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
210                                         <Field
211                                             label="E-mail at Organization"
212                                             name="prefs.profile.organization_email"
213                                             component={TextField as any}
214                                             validate={MY_ACCOUNT_VALIDATION}
215                                             required
216                                         />
217                                     </Grid>
218                                     <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
219                                         <InputLabel className={this.props.classes.label} htmlFor="prefs.profile.role">Role</InputLabel>
220                                         <Field
221                                             id="prefs.profile.role"
222                                             name="prefs.profile.role"
223                                             component={NativeSelectField as any}
224                                             items={RoleTypes}
225                                         />
226                                     </Grid>
227                                     <Grid item className={this.props.classes.gridItem} sm={6} xs={12}>
228                                         <Field
229                                             label="Website"
230                                             name="prefs.profile.website_url"
231                                             component={TextField as any}
232                                         />
233                                     </Grid>
234                                     <Grid item sm={12}>
235                                         <Grid container direction="row" justify="flex-end">
236                                             <Button color="primary" onClick={this.props.reset} disabled={this.props.isPristine}>Discard changes</Button>
237                                             <Button
238                                                 color="primary"
239                                                 variant="contained"
240                                                 type="submit"
241                                                 disabled={this.props.isPristine || this.props.invalid || this.props.submitting}>
242                                                 Save changes
243                                             </Button>
244                                         </Grid>
245                                     </Grid>
246                                 </Grid>
247                             </form >
248                         </CardContent>
249                     // </Card>
250                 }
251                 {this.state.value === 1 &&
252                     <div className={this.props.classes.content}>
253                         <DataExplorer
254                                 id={USER_PROFILE_PANEL_ID}
255                                 onRowClick={noop}
256                                 onRowDoubleClick={noop}
257                                 // onContextMenu={this.handleContextMenu}
258                                 contextMenuColumn={false}
259                                 hideColumnSelector
260                                 hideSearchInput
261                                 paperProps={{
262                                     elevation: 0,
263                                 }}
264                                 dataTableDefaultView={
265                                     <DataTableDefaultView
266                                         icon={GroupsIcon}
267                                         messages={['Group list is empty.']} />
268                                 } />
269                     </div>}
270                 {this.state.value === 2 &&
271                     <Paper elevation={0} className={this.props.classes.adminRoot}>
272                         <Card elevation={0}>
273                             <CardContent>
274                                 <Grid container
275                                     direction="row"
276                                     justify={'flex-end'}
277                                     alignItems={'center'}>
278                                     <Grid item xs>
279                                         <Typography variant="h6" className={this.props.classes.title}>
280                                             Setup Account
281                                         </Typography>
282                                         <Typography variant="body1" className={this.props.classes.description}>
283                                             This button sets up a user. After setup, they will be able use Arvados. This dialog box also allows you to optionally set up a shell account for this user. The login name is automatically generated from the user's e-mail address.
284                                         </Typography>
285                                     </Grid>
286                                     <Grid item sm={'auto'} xs={12}>
287                                         <Button variant="contained"
288                                             color="primary"
289                                             onClick={() => {this.props.openSetupShellAccount(this.props.initialValues.uuid)}}
290                                             disabled={false}>
291                                             Setup Account
292                                         </Button>
293                                     </Grid>
294                                 </Grid>
295                             </CardContent>
296                         </Card>
297                         <Card elevation={0}>
298                             <CardContent>
299                                 <Grid container
300                                     direction="row"
301                                     justify={'flex-end'}
302                                     alignItems={'center'}>
303                                     <Grid item xs>
304                                         <Typography variant="h6" className={this.props.classes.title}>
305                                             Deactivate
306                                         </Typography>
307                                         <Typography variant="body1" className={this.props.classes.description}>
308                                             As an admin, you can deactivate and reset this user. This will remove all repository/VM permissions for the user. If you "setup" the user again, the user will have to sign the user agreement again. You may also want to reassign data ownership.
309                                         </Typography>
310                                     </Grid>
311                                     <Grid item sm={'auto'} xs={12}>
312                                         <Button variant="contained"
313                                             color="primary"
314                                             onClick={() => {this.props.openDeactivateDialog(this.props.initialValues.uuid)}}
315                                             disabled={false}>
316                                             Deactivate
317                                         </Button>
318                                     </Grid>
319                                 </Grid>
320                             </CardContent>
321                         </Card>
322                         <Card elevation={0}>
323                             <CardContent>
324                                 <Grid container
325                                     direction="row"
326                                     justify={'flex-end'}
327                                     alignItems={'center'}>
328                                     <Grid item xs>
329                                         <Typography variant="h6" className={this.props.classes.title}>
330                                             Log In
331                                         </Typography>
332                                         <Typography variant="body1" className={this.props.classes.description}>
333                                             As an admin, you can log in as this user. When you’ve finished, you will need to log out and log in again with your own account.
334                                         </Typography>
335                                     </Grid>
336                                     <Grid item sm={'auto'} xs={12}>
337                                         <Button variant="contained"
338                                             color="primary"
339                                             onClick={() => {this.props.loginAs(this.props.initialValues.uuid)}}
340                                             disabled={false}>
341                                             Log In
342                                         </Button>
343                                     </Grid>
344                                 </Grid>
345                             </CardContent>
346                         </Card>
347                     </Paper>}
348             </Paper >;
349         }
350
351         handleChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
352             this.setState({ value });
353         }
354
355         handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
356             // const resource = getResource<UserResource>(resourceUuid)(this.props.resources);
357             // if (resource) {
358             //     this.props.onContextMenu(event, {
359             //         name: '',
360             //         uuid: resource.uuid,
361             //         ownerUuid: resource.ownerUuid,
362             //         kind: resource.kind,
363             //         menuKind: ContextMenuKind.USER
364             //     });
365             // }
366         }
367     }
368 );