15088: Adds unique message for inactive remote users linking accounts
[arvados-workbench2.git] / src / views / link-account-panel / link-account-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     StyleRulesCallback,
8     WithStyles,
9     withStyles,
10     Card,
11     CardContent,
12     Button,
13     Grid,
14     Select,
15     CircularProgress
16 } from '@material-ui/core';
17 import { ArvadosTheme } from '~/common/custom-theme';
18 import { UserResource } from "~/models/user";
19 import { LinkAccountType } from "~/models/link-account";
20 import { formatDate } from "~/common/formatters";
21 import { LinkAccountPanelStatus, LinkAccountPanelError } from "~/store/link-account-panel/link-account-panel-reducer";
22
23 type CssRules = 'root';
24
25 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
26     root: {
27         width: '100%',
28         overflow: 'auto'
29     }
30 });
31
32 export interface LinkAccountPanelRootDataProps {
33     targetUser?: UserResource;
34     userToLink?: UserResource;
35     remoteHosts:  { [key: string]: string };
36     hasRemoteHosts: boolean;
37     localCluster: string;
38     status : LinkAccountPanelStatus;
39     error: LinkAccountPanelError;
40     selectedCluster?: string;
41     isProcessing: boolean;
42 }
43
44 export interface LinkAccountPanelRootActionProps {
45     startLinking: (type: LinkAccountType) => void;
46     cancelLinking: () => void;
47     linkAccount: () => void;
48     setSelectedCluster: (cluster: string) => void;
49 }
50
51 function displayUser(user: UserResource, showCreatedAt: boolean = false, showCluster: boolean = false) {
52     const disp = [];
53     disp.push(<span><b>{user.email}</b> ({user.username}, {user.uuid})</span>);
54     if (showCluster) {
55         const homeCluster = user.uuid.substr(0,5);
56         disp.push(<span> hosted on cluster <b>{homeCluster}</b> and </span>);
57     }
58     if (showCreatedAt) {
59         disp.push(<span> created on <b>{formatDate(user.createdAt, true)}</b></span>);
60     }
61     return disp;
62 }
63
64 function isLocalUser(uuid: string, localCluster: string) {
65     return uuid.substring(0, 5) === localCluster;
66 }
67
68 type LinkAccountPanelRootProps = LinkAccountPanelRootDataProps & LinkAccountPanelRootActionProps & WithStyles<CssRules>;
69
70 export const LinkAccountPanelRoot = withStyles(styles) (
71     ({classes, targetUser, userToLink, status, isProcessing, error, startLinking, cancelLinking, linkAccount,
72       remoteHosts, hasRemoteHosts, selectedCluster, setSelectedCluster, localCluster}: LinkAccountPanelRootProps) => {
73         return <Card className={classes.root}>
74             <CardContent>
75             { isProcessing && <Grid container item direction="column" alignContent="center" spacing={24}>
76                 <Grid item>
77                     Loading user info. Please wait.
78                 </Grid>
79                 <Grid item style={{ alignSelf: 'center' }}>
80                     <CircularProgress/>
81                 </Grid>
82             </Grid> }
83             { status === LinkAccountPanelStatus.INITIAL && targetUser && <div>
84                 { isLocalUser(targetUser.uuid, localCluster) ? <Grid container spacing={24}>
85                     <Grid container item direction="column" spacing={24}>
86                         <Grid item>
87                             You are currently logged in as {displayUser(targetUser, true)}
88                         </Grid>
89                         <Grid item>
90                             You can link Arvados accounts. After linking, either login will take you to the same account.
91                         </Grid >
92                     </Grid>
93                     <Grid container item direction="row" spacing={24}>
94                         <Grid item>
95                             <Button disabled={!targetUser.isActive} color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ADD_OTHER_LOGIN)}>
96                                 Add another login to this account
97                             </Button>
98                         </Grid>
99                         <Grid item>
100                             <Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ACCESS_OTHER_ACCOUNT)}>
101                                 Use this login to access another account
102                             </Button>
103                         </Grid>
104                     </Grid>
105                     { hasRemoteHosts && selectedCluster && <Grid container item direction="column" spacing={24}>
106                         <Grid item>
107                             You can also link {displayUser(targetUser, false)} with an account from a remote cluster.
108                         </Grid>
109                         <Grid item>
110                             Please select the cluster that hosts the account you want to link with:
111                                 <Select id="remoteHostsDropdown" native defaultValue={selectedCluster} style={{ marginLeft: "1em" }}
112                                     onChange={(event) => setSelectedCluster(event.target.value)}>
113                                     {Object.keys(remoteHosts).map((k) => k !== localCluster ? <option key={k} value={k}>{k}</option> : null)}
114                                 </Select>
115                             </Grid>
116                         <Grid item>
117                             <Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ACCESS_OTHER_REMOTE_ACCOUNT)}>
118                                 Link with an account on&nbsp;{hasRemoteHosts ? <label>{selectedCluster} </label> : null}
119                             </Button>
120                         </Grid>
121                     </Grid> }
122                 </Grid> :
123                 <Grid container spacing={24}>
124                     <Grid container item direction="column" spacing={24}>
125                         <Grid item>
126                             You are currently logged in as {displayUser(targetUser, true, true)}
127                         </Grid>
128                         {targetUser.isActive ? <> <Grid item>
129                             This a remote account. You can link a local Arvados account to this one. After linking, you can access the local account's data by logging into the <b>{localCluster}</b> cluster with the <b>{targetUser.email}</b> account.
130                         </Grid >
131                         <Grid item>
132                             <Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ADD_LOCAL_TO_REMOTE)}>
133                                 Link an account from {localCluster} to this account
134                             </Button>
135                         </Grid> </>
136                         : <Grid item>
137                           This an inactive remote account. An administrator must activate your account before you can proceed. After your accounts is activated, you can link a local Arvados account hosted by the <b>{localCluster}</b> cluster to this one.
138                         </Grid >}
139                     </Grid>
140                 </Grid>}
141             </div> }
142             { (status === LinkAccountPanelStatus.LINKING || status === LinkAccountPanelStatus.ERROR) && userToLink && targetUser &&
143             <Grid container spacing={24}>
144                 { status === LinkAccountPanelStatus.LINKING && <Grid container item direction="column" spacing={24}>
145                     <Grid item>
146                         Clicking 'Link accounts' will link {displayUser(userToLink, true, !isLocalUser(targetUser.uuid, localCluster))} to {displayUser(targetUser, true, !isLocalUser(targetUser.uuid, localCluster))}.
147                     </Grid>
148                     { (isLocalUser(targetUser.uuid, localCluster)) && <Grid item>
149                         After linking, logging in as {displayUser(userToLink)} will log you into the same account as {displayUser(targetUser)}.
150                     </Grid> }
151                     <Grid item>
152                         Any object owned by {displayUser(userToLink)} will be transfered to {displayUser(targetUser)}.
153                     </Grid>
154                     { !isLocalUser(targetUser.uuid, localCluster) && <Grid item>
155                         You can access <b>{userToLink.email}</b> data by logging into <b>{localCluster}</b> with the <b>{targetUser.email}</b> account.
156                     </Grid> }
157                 </Grid> }
158                 { error === LinkAccountPanelError.NON_ADMIN && <Grid item>
159                     Cannot link admin account {displayUser(userToLink)} to non-admin account {displayUser(targetUser)}.
160                 </Grid> }
161                 { error === LinkAccountPanelError.SAME_USER && <Grid item>
162                     Cannot link {displayUser(targetUser)} to the same account.
163                 </Grid> }
164                 { error === LinkAccountPanelError.INACTIVE && <Grid item>
165                     Cannot link account {displayUser(userToLink)} to inactive account {displayUser(targetUser)}.
166                 </Grid> }
167                 <Grid container item direction="row" spacing={24}>
168                     <Grid item>
169                         <Button variant="contained" onClick={() => cancelLinking()}>
170                             Cancel
171                         </Button>
172                     </Grid>
173                     <Grid item>
174                         <Button disabled={status === LinkAccountPanelStatus.ERROR} color="primary" variant="contained" onClick={() => linkAccount()}>
175                             Link accounts
176                         </Button>
177                     </Grid>
178                 </Grid>
179             </Grid> }
180         </CardContent>
181     </Card>;
182 });