74e445503d250874a1e9b6ca0d85a2f47f293291
[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                 }>
119                 <Tab label="With users/groups" />
120                 <Tab label={`Sharing URLs ${sharingURLsNr > 0 ? '(' + sharingURLsNr + ')' : ''}`} disabled={saveEnabled} />
121             </Tabs>
122         }
123         <DialogContent>
124             {tabNr === SharingDialogTab.PERMISSIONS &&
125                 <Grid container direction='column' spacing={24}>
126                     <Grid item>
127                         <SharingPublicAccessForm />
128                     </Grid>
129                     <Grid item>
130                         <SharingManagementForm />
131                     </Grid>
132                 </Grid>
133             }
134             {tabNr === SharingDialogTab.URLS &&
135                 <SharingURLsContent uuid={sharedResourceUuid} />
136             }
137         </DialogContent>
138         <DialogActions>
139             <Grid container spacing={8}>
140                 {tabNr === SharingDialogTab.PERMISSIONS &&
141                     <Grid item md={12}>
142                         <SharingInvitationForm onSave={onSave} saveEnabled={saveEnabled} />
143                     </Grid>
144                 }
145                 {tabNr === SharingDialogTab.URLS && withExpiration && <>
146                     <Grid item container direction='row' md={12}>
147                         <MuiPickersUtilsProvider utils={DateFnsUtils}>
148                             <BasePicker autoOk value={expDate} onChange={setExpDate}>
149                                 {({ date, handleChange }) => (<>
150                                     <Grid item md={6}>
151                                         <Calendar date={date} minDate={new Date()} maxDate={undefined}
152                                             onChange={handleChange} />
153                                     </Grid>
154                                     <Grid item md={6}>
155                                         <TimePickerView type="hours" date={date} ampm={false}
156                                             onMinutesChange={() => { }}
157                                             onSecondsChange={() => { }}
158                                             onHourChange={handleChange}
159                                         />
160                                     </Grid>
161                                 </>)}
162                             </BasePicker>
163                         </MuiPickersUtilsProvider>
164                     </Grid>
165                     <Grid item md={12}>
166                         <Typography variant='caption' align='center'>
167                             Maximum expiration date may be limited by the cluster configuration.
168                         </Typography>
169                     </Grid>
170                 </>
171                 }
172                 {tabNr === SharingDialogTab.PERMISSIONS && !sharingURLsDisabled &&
173                     privateAccess && sharingURLsNr > 0 &&
174                     <Grid item md={12}>
175                         <Typography variant='caption' align='center' color='error'>
176                             Although there aren't specific permissions set, this is publicly accessible via Sharing URL(s).
177                         </Typography>
178                     </Grid>
179                 }
180                 <Grid item xs />
181                 {tabNr === SharingDialogTab.URLS && <>
182                     <Grid item><FormControlLabel
183                         control={<Checkbox color="primary" checked={withExpiration}
184                             onChange={(e) => setWithExpiration(e.target.checked)} />}
185                         label="With expiration" />
186                     </Grid>
187                     <Grid item>
188                         <Button variant="contained" color="primary"
189                             disabled={expDate !== undefined && expDate <= new Date()}
190                             onClick={onCreateSharingToken(expDate)}>
191                             Create sharing URL
192                         </Button>
193                     </Grid>
194                 </>
195                 }
196                 <Grid item>
197                     <Button onClick={() => {
198                         onClose();
199                         setWithExpiration(false);
200                     }}>
201                         Close
202                     </Button>
203                 </Grid>
204             </Grid>
205         </DialogActions>
206         {
207             loading && <LoadingIndicator />
208         }
209     </Dialog>;
210 };
211
212 const loadingIndicatorStyles: StyleRulesCallback<'root'> = theme => ({
213     root: {
214         position: 'absolute',
215         top: 0,
216         right: 0,
217         bottom: 0,
218         left: 0,
219         display: 'flex',
220         alignItems: 'center',
221         justifyContent: 'center',
222         backgroundColor: 'rgba(255, 255, 255, 0.8)',
223     },
224 });
225
226 const LoadingIndicator = withStyles(loadingIndicatorStyles)(
227     (props: WithStyles<'root'>) =>
228         <Paper classes={props.classes}>
229             <CircularProgress />
230         </Paper>
231 );