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