200085: Added changes to share dialog layout
[arvados-workbench2.git] / src / views-components / sharing-dialog / sharing-dialog-component.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     Dialog,
8     DialogTitle,
9     Button,
10     Grid,
11     DialogContent,
12     CircularProgress,
13     Paper,
14     Tabs,
15     Tab,
16     Checkbox,
17     FormControlLabel,
18     Typography,
19     Tooltip,
20 } from '@material-ui/core';
21 import {
22     StyleRulesCallback,
23     WithStyles,
24     withStyles
25 } from '@material-ui/core/styles';
26 import { DialogActions } from 'components/dialog-actions/dialog-actions';
27 import { SharingURLsContent } from './sharing-urls';
28 import {
29     extractUuidObjectType,
30     ResourceObjectType
31 } from 'models/resource';
32 import { SharingInvitationForm } from './sharing-invitation-form';
33 import { SharingManagementForm } from './sharing-management-form';
34 import {
35     BasePicker,
36     Calendar,
37     MuiPickersUtilsProvider,
38     TimePickerView
39 } from 'material-ui-pickers';
40 import DateFnsUtils from "@date-io/date-fns";
41 import moment from 'moment';
42 import { SharingPublicAccessForm } from './sharing-public-access-form';
43 import { AddIcon } from 'components/icon/icon';
44
45 export interface SharingDialogDataProps {
46     open: boolean;
47     loading: boolean;
48     saveEnabled: boolean;
49     sharedResourceUuid: string;
50     sharingURLsNr: number;
51     privateAccess: boolean;
52     sharingURLsDisabled: boolean;
53     permissions: any[];
54 }
55 export interface SharingDialogActionProps {
56     onClose: () => void;
57     onSave: () => void;
58     onCreateSharingToken: (d: Date | undefined) => () => void;
59     refreshPermissions: () => void;
60 }
61 enum SharingDialogTab {
62     PERMISSIONS = 0,
63     URLS = 1,
64 }
65 export type SharingDialogComponentProps = SharingDialogDataProps & SharingDialogActionProps;
66
67 export default (props: SharingDialogComponentProps) => {
68     const { open, loading, saveEnabled, sharedResourceUuid,
69         sharingURLsNr, privateAccess, sharingURLsDisabled, permissions,
70         onClose, onSave, onCreateSharingToken, refreshPermissions } = props;
71     const showTabs = !sharingURLsDisabled && extractUuidObjectType(sharedResourceUuid) === ResourceObjectType.COLLECTION;
72     const [tabNr, setTabNr] = React.useState<number>(SharingDialogTab.PERMISSIONS);
73     const [expDate, setExpDate] = React.useState<Date>();
74     const [withExpiration, setWithExpiration] = React.useState<boolean>(false);
75     const [permissionsCount, setPermissionsCount] = React.useState<number>(0);
76
77     // Sets up the dialog depending on the resource type
78     if (!showTabs && tabNr !== SharingDialogTab.PERMISSIONS) {
79         setTabNr(SharingDialogTab.PERMISSIONS);
80     }
81
82     React.useEffect(() => {
83         if (permissions && permissions.length !== permissionsCount) {
84             if (permissionsCount > permissions.length) {
85                 setTimeout(onSave, 0);
86             }
87
88             setPermissionsCount(permissions.length);
89         }
90     }, [permissions, onSave, setPermissionsCount, permissionsCount])
91
92     React.useEffect(() => {
93         if (!withExpiration) {
94             setExpDate(undefined);
95         } else {
96             setExpDate(moment().add(2, 'hour').minutes(0).seconds(0).toDate());
97         }
98     }, [withExpiration]);
99
100     return <Dialog
101         {...{ open, onClose }}
102         className="sharing-dialog"
103         fullWidth
104         maxWidth='sm'
105         disableBackdropClick={saveEnabled}
106         disableEscapeKeyDown={saveEnabled}>
107         <DialogTitle>
108             Sharing settings
109         </DialogTitle>
110         { showTabs &&
111         <Tabs value={tabNr}
112             onChange={(_, tb) => {
113                 if (tb === SharingDialogTab.PERMISSIONS) {
114                     refreshPermissions();
115                 }
116                 setTabNr(tb)}
117             }>
118             <Tab label="With users/groups" />
119             <Tab label={`Sharing URLs ${sharingURLsNr > 0 ? '('+sharingURLsNr+')' : ''}`} disabled={saveEnabled} />
120         </Tabs>
121         }
122         <DialogContent>
123             { tabNr === SharingDialogTab.PERMISSIONS &&
124             <Grid container direction='column' spacing={24}>
125                 <Grid item>
126                     <SharingPublicAccessForm />
127                 </Grid>
128                 <Grid item>
129                     <SharingManagementForm />
130                 </Grid>
131             </Grid>
132             }
133             { tabNr === SharingDialogTab.URLS &&
134             <SharingURLsContent uuid={sharedResourceUuid} />
135             }
136         </DialogContent>
137         <DialogActions>
138             <Grid container spacing={8}>
139                 { tabNr === SharingDialogTab.PERMISSIONS &&
140                 <Grid item md={12}>
141                     <SharingInvitationForm />
142                 </Grid>
143                 }
144                 { tabNr === SharingDialogTab.URLS && withExpiration && <>
145                 <Grid item container direction='row' md={12}>
146                     <MuiPickersUtilsProvider utils={DateFnsUtils}>
147                         <BasePicker autoOk value={expDate} onChange={setExpDate}>
148                         {({ date, handleChange }) => (<>
149                             <Grid item md={6}>
150                                 <Calendar date={date} minDate={new Date()} maxDate={undefined}
151                                     onChange={handleChange} />
152                             </Grid>
153                             <Grid item md={6}>
154                                 <TimePickerView type="hours" date={date} ampm={false}
155                                     onMinutesChange={() => {}}
156                                     onSecondsChange={() => {}}
157                                     onHourChange={handleChange}
158                                 />
159                             </Grid>
160                         </>)}
161                         </BasePicker>
162                     </MuiPickersUtilsProvider>
163                 </Grid>
164                 <Grid item md={12}>
165                     <Typography variant='caption' align='center'>
166                         Maximum expiration date may be limited by the cluster configuration.
167                     </Typography>
168                 </Grid>
169                 </>
170                 }
171                 { tabNr === SharingDialogTab.PERMISSIONS && !sharingURLsDisabled &&
172                     privateAccess && sharingURLsNr > 0 &&
173                 <Grid item md={12}>
174                     <Typography variant='caption' align='center' color='error'>
175                         Although there aren't specific permissions set, this is publicly accessible via Sharing URL(s).
176                     </Typography>
177                 </Grid>
178                 }
179                 <Grid item xs />
180                 { tabNr === SharingDialogTab.URLS && <>
181                 <Grid item><FormControlLabel
182                     control={<Checkbox color="primary" checked={withExpiration}
183                         onChange={(e) => setWithExpiration(e.target.checked)} />}
184                     label="With expiration" />
185                 </Grid>
186                 <Grid item>
187                     <Button variant="contained" color="primary"
188                         disabled={expDate !== undefined && expDate <= new Date()}
189                         onClick={onCreateSharingToken(expDate)}>
190                         Create sharing URL
191                     </Button>
192                 </Grid>
193                 </>
194                 }
195                 { tabNr === SharingDialogTab.PERMISSIONS &&
196                 <Grid item>
197                     <Tooltip title="Add authorization">
198                         <Button onClick={onSave} variant="contained" color="primary"
199                             disabled={!saveEnabled}>
200                             <AddIcon />
201                         </Button>
202                     </Tooltip>
203                 </Grid>
204                 }
205                 <Grid item>
206                     <Button onClick={() => {
207                         onClose();
208                         setWithExpiration(false);
209                     }}>
210                         Close
211                     </Button>
212                 </Grid>
213             </Grid>
214         </DialogActions>
215         {
216             loading && <LoadingIndicator />
217         }
218     </Dialog>;
219 };
220
221 const loadingIndicatorStyles: StyleRulesCallback<'root'> = theme => ({
222     root: {
223         position: 'absolute',
224         top: 0,
225         right: 0,
226         bottom: 0,
227         left: 0,
228         display: 'flex',
229         alignItems: 'center',
230         justifyContent: 'center',
231         backgroundColor: 'rgba(255, 255, 255, 0.8)',
232     },
233 });
234
235 const LoadingIndicator = withStyles(loadingIndicatorStyles)(
236     (props: WithStyles<'root'>) =>
237         <Paper classes={props.classes}>
238             <CircularProgress />
239         </Paper>
240 );