21700: Install Bundler system-wide in Rails postinst
[arvados.git] / services / workbench2 / 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 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     localClusterConfig: Config;
98 }
99
100 type SiteManagerPanelRootProps = SiteManagerPanelRootDataProps & SiteManagerPanelRootActionProps & WithStyles<CssRules> & InjectedFormProps;
101 const SITE_MANAGER_FORM_NAME = 'siteManagerForm';
102
103 const submitSession = (remoteHost: string) =>
104     (dispatch: Dispatch) => {
105         dispatch<any>(addSession(remoteHost, undefined, true)).then(() => {
106             dispatch(reset(SITE_MANAGER_FORM_NAME));
107         }).catch((e: any) => {
108             const errors = {
109                 remoteHost: e
110             } as FormErrors;
111             dispatch(stopSubmit(SITE_MANAGER_FORM_NAME, errors));
112         });
113     };
114
115 export const SiteManagerPanelRoot = compose(
116     reduxForm<{ remoteHost: string }>({
117         form: SITE_MANAGER_FORM_NAME,
118         touchOnBlur: false,
119         onSubmit: (data, dispatch) => {
120             dispatch(submitSession(data.remoteHost));
121         }
122     }),
123     withStyles(styles))
124     (({ classes, sessions, handleSubmit, toggleSession, removeSession, localClusterConfig, remoteHostsConfig }: SiteManagerPanelRootProps) =>
125         <Card className={classes.root}>
126             <CardContent>
127                 <Grid container direction="row">
128                     <Grid item xs={12}>
129                         <Typography paragraph={true} >
130                             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.
131                     </Typography>
132                     </Grid>
133                 </Grid>
134                 <Grid item xs={12}>
135                     {sessions.length > 0 && <Table className={classes.table}>
136                         <TableHead>
137                             <TableRow className={classes.tableRow}>
138                                 <TableCell>Cluster ID</TableCell>
139                                 <TableCell>Host</TableCell>
140                                 <TableCell>Email</TableCell>
141                                 <TableCell>UUID</TableCell>
142                                 <TableCell>Status</TableCell>
143                                 <TableCell>Actions</TableCell>
144                             </TableRow>
145                         </TableHead>
146                         <TableBody>
147                             {sessions.map((session, index) => {
148                                 const validating = session.status === SessionStatus.BEING_VALIDATED;
149                                 return <TableRow key={index} className={classes.tableRow}>
150                                     <TableCell>{remoteHostsConfig[session.clusterId] ?
151                                         <a href={remoteHostsConfig[session.clusterId].workbench2Url} style={{ textDecoration: 'none' }}> <ResourceCluster uuid={session.clusterId} /></a>
152                                         : session.clusterId}</TableCell>
153                                     <TableCell>{session.remoteHost}</TableCell>
154                                     <TableCell>{validating ? <CircularProgress size={20} /> : session.email}</TableCell>
155                                     <TableCell>{validating ? <CircularProgress size={20} /> : session.uuid}</TableCell>
156                                     <TableCell className={classes.statusCell}>
157                                         <Button fullWidth
158                                             disabled={validating || session.status === SessionStatus.INVALIDATED || session.active}
159                                             className={session.loggedIn ? classes.buttonLoggedIn : classes.buttonLoggedOut}
160                                             onClick={() => toggleSession(session)}>
161                                             {validating ? "Validating"
162                                                 : (session.loggedIn ?
163                                                     (session.userIsActive ? "Logged in" : "Inactive")
164                                                     : "Logged out")}
165                                         </Button>
166                                     </TableCell>
167                                     <TableCell>
168                                         {session.clusterId !== localClusterConfig.uuidPrefix &&
169                                             !localClusterConfig.clusterConfig.RemoteClusters[session.clusterId] &&
170                                             <IconButton onClick={() => removeSession(session)}>
171                                                 <TrashIcon />
172                                             </IconButton>}
173                                     </TableCell>
174                                 </TableRow>;
175                             })}
176                         </TableBody>
177                     </Table>}
178                 </Grid>
179                 <form onSubmit={handleSubmit}>
180                     <Grid container direction="row">
181                         <Grid item xs={12}>
182                             <Typography paragraph={true} className={classes.remoteSiteInfo}>
183                                 To add a remote Arvados site, paste the remote site's host here (see "ARVADOS_API_HOST" on the "current token" page).
184                         </Typography>
185                         </Grid>
186                         <Grid item xs={8}>
187                             <Field
188                                 name='remoteHost'
189                                 validate={SITE_MANAGER_REMOTE_HOST_VALIDATION}
190                                 component={TextField as any}
191                                 placeholder="zzzz.arvadosapi.com"
192                                 margin="normal"
193                                 label="New cluster"
194                                 autoFocus />
195                         </Grid>
196                         <Grid item xs={3}>
197                             <Button type="submit" variant="contained" color="primary"
198                                 className={classes.buttonAdd}>
199                                 {"ADD"}</Button>
200                         </Grid>
201                     </Grid>
202                 </form>
203             </CardContent>
204         </Card>
205     );