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