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