15736: Add delete button to site manager
[arvados-workbench2.git] / src / views / site-manager-panel / site-manager-panel-root.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import {
7     Card,
8     CardContent,
9     CircularProgress,
10     Grid,
11     IconButton,
12     StyleRulesCallback,
13     Table,
14     TableBody,
15     TableCell,
16     TableHead,
17     TableRow,
18     Typography,
19     WithStyles,
20     withStyles
21 } from '@material-ui/core';
22 import { ArvadosTheme } from '~/common/custom-theme';
23 import { Session, SessionStatus } from "~/models/session";
24 import Button from "@material-ui/core/Button";
25 import { compose, Dispatch } from "redux";
26 import { Field, FormErrors, InjectedFormProps, reduxForm, reset, stopSubmit } from "redux-form";
27 import { TextField } from "~/components/text-field/text-field";
28 import { addSession } from "~/store/auth/auth-action-session";
29 import { SITE_MANAGER_REMOTE_HOST_VALIDATION } from "~/validators/validators";
30 import { Config } from '~/common/config';
31 import { ResourceCluster } from '~/views-components/data-explorer/renderers';
32 import { TrashIcon } from "~/components/icon/icon";
33
34 type CssRules = 'root' | 'link' | 'buttonContainer' | 'table' | 'tableRow' |
35     'remoteSiteInfo' | 'buttonAdd' | 'buttonLoggedIn' | 'buttonLoggedOut' |
36     'statusCell';
37
38 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
39     root: {
40         width: '100%',
41         overflow: 'auto'
42     },
43     link: {
44         color: theme.palette.primary.main,
45         textDecoration: 'none',
46         margin: '0px 4px'
47     },
48     buttonContainer: {
49         textAlign: 'right'
50     },
51     table: {
52         marginTop: theme.spacing.unit
53     },
54     tableRow: {
55         '& td, th': {
56             whiteSpace: 'nowrap'
57         }
58     },
59     statusCell: {
60         minWidth: 160
61     },
62     remoteSiteInfo: {
63         marginTop: 20
64     },
65     buttonAdd: {
66         marginLeft: 10,
67         marginTop: theme.spacing.unit * 3
68     },
69     buttonLoggedIn: {
70         minHeight: theme.spacing.unit,
71         padding: 5,
72         color: '#fff',
73         backgroundColor: '#009966',
74         '&:hover': {
75             backgroundColor: '#008450',
76         }
77     },
78     buttonLoggedOut: {
79         minHeight: theme.spacing.unit,
80         padding: 5,
81         color: '#000',
82         backgroundColor: '#FFC414',
83         '&:hover': {
84             backgroundColor: '#eaaf14',
85         }
86     }
87 });
88
89 export interface SiteManagerPanelRootActionProps {
90     toggleSession: (session: Session) => void;
91     removeSession: (session: Session) => void;
92 }
93
94 export interface SiteManagerPanelRootDataProps {
95     sessions: Session[];
96     remoteHostsConfig: { [key: string]: Config };
97 }
98
99 type SiteManagerPanelRootProps = SiteManagerPanelRootDataProps & SiteManagerPanelRootActionProps & WithStyles<CssRules> & InjectedFormProps;
100 const SITE_MANAGER_FORM_NAME = 'siteManagerForm';
101
102 const submitSession = (remoteHost: string) =>
103     (dispatch: Dispatch) => {
104         dispatch<any>(addSession(remoteHost, undefined, true)).then(() => {
105             dispatch(reset(SITE_MANAGER_FORM_NAME));
106         }).catch((e: any) => {
107             const errors = {
108                 remoteHost: e
109             } as FormErrors;
110             dispatch(stopSubmit(SITE_MANAGER_FORM_NAME, errors));
111         });
112     };
113
114 export const SiteManagerPanelRoot = compose(
115     reduxForm<{ remoteHost: string }>({
116         form: SITE_MANAGER_FORM_NAME,
117         touchOnBlur: false,
118         onSubmit: (data, dispatch) => {
119             dispatch(submitSession(data.remoteHost));
120         }
121     }),
122     withStyles(styles))
123     (({ classes, sessions, handleSubmit, toggleSession, removeSession, remoteHostsConfig }: SiteManagerPanelRootProps) =>
124         <Card className={classes.root}>
125             <CardContent>
126                 <Grid container direction="row">
127                     <Grid item xs={12}>
128                         <Typography paragraph={true} >
129                             You can log in to multiple Arvados sites here, then use the multi-site search page to search collections and projects on all sites at once.
130                     </Typography>
131                     </Grid>
132                 </Grid>
133                 <Grid item xs={12}>
134                     {sessions.length > 0 && <Table className={classes.table}>
135                         <TableHead>
136                             <TableRow className={classes.tableRow}>
137                                 <TableCell>Cluster ID</TableCell>
138                                 <TableCell>Host</TableCell>
139                                 <TableCell>Email</TableCell>
140                                 <TableCell>UUID</TableCell>
141                                 <TableCell>Status</TableCell>
142                                 <TableCell>Actions</TableCell>
143                             </TableRow>
144                         </TableHead>
145                         <TableBody>
146                             {sessions.map((session, index) => {
147                                 const validating = session.status === SessionStatus.BEING_VALIDATED;
148                                 return <TableRow key={index} className={classes.tableRow}>
149                                     <TableCell>{remoteHostsConfig[session.clusterId] ?
150                                         <a href={remoteHostsConfig[session.clusterId].workbench2Url} style={{ textDecoration: 'none' }}> <ResourceCluster uuid={session.clusterId} /></a>
151                                         : session.clusterId}</TableCell>
152                                     <TableCell>{session.remoteHost}</TableCell>
153                                     <TableCell>{validating ? <CircularProgress size={20} /> : session.email}</TableCell>
154                                     <TableCell>{validating ? <CircularProgress size={20} /> : session.uuid}</TableCell>
155                                     <TableCell className={classes.statusCell}>
156                                         <Button fullWidth
157                                             disabled={validating || session.status === SessionStatus.INVALIDATED || session.active}
158                                             className={session.loggedIn ? classes.buttonLoggedIn : classes.buttonLoggedOut}
159                                             onClick={() => toggleSession(session)}>
160                                             {validating ? "Validating" : (session.loggedIn ? "Logged in" : "Logged out")}
161                                         </Button>
162                                     </TableCell>
163                                     <IconButton onClick={() => removeSession(session)}>
164                                         <TrashIcon />
165                                     </IconButton>
166                                 </TableRow>;
167                             })}
168                         </TableBody>
169                     </Table>}
170                 </Grid>
171                 <form onSubmit={handleSubmit}>
172                     <Grid container direction="row">
173                         <Grid item xs={12}>
174                             <Typography paragraph={true} className={classes.remoteSiteInfo}>
175                                 To add a remote Arvados site, paste the remote site's host here (see "ARVADOS_API_HOST" on the "current token" page).
176                         </Typography>
177                         </Grid>
178                         <Grid item xs={8}>
179                             <Field
180                                 name='remoteHost'
181                                 validate={SITE_MANAGER_REMOTE_HOST_VALIDATION}
182                                 component={TextField}
183                                 placeholder="zzzz.arvadosapi.com"
184                                 margin="normal"
185                                 label="New cluster"
186                                 autoFocus />
187                         </Grid>
188                         <Grid item xs={3}>
189                             <Button type="submit" variant="contained" color="primary"
190                                 className={classes.buttonAdd}>
191                                 {"ADD"}</Button>
192                         </Grid>
193                     </Grid>
194                 </form>
195             </CardContent>
196         </Card>
197     );