df232724c55e8ac6573ef530cc4963ef2f1de3f7
[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
76     // Sets up the dialog depending on the resource type
77     if (!showTabs && tabNr !== SharingDialogTab.PERMISSIONS) {
78         setTabNr(SharingDialogTab.PERMISSIONS);
79     }
80
81     React.useEffect(() => {
82         if (!withExpiration) {
83             setExpDate(undefined);
84         } else {
85             setExpDate(moment().add(2, 'hour').minutes(0).seconds(0).toDate());
86         }
87     }, [withExpiration]);
88
89     return <Dialog
90         {...{ open, onClose }}
91         className="sharing-dialog"
92         fullWidth
93         maxWidth='sm'
94         disableBackdropClick={saveEnabled}
95         disableEscapeKeyDown={saveEnabled}>
96         <DialogTitle>
97             Sharing settings
98         </DialogTitle>
99         {showTabs &&
100             <Tabs value={tabNr}
101                 onChange={(_, tb) => {
102                     if (tb === SharingDialogTab.PERMISSIONS) {
103                         refreshPermissions();
104                     }
105                     setTabNr(tb)
106                 }
107                 }>
108                 <Tab label="With users/groups" />
109                 <Tab label={`Sharing URLs ${sharingURLsNr > 0 ? '(' + sharingURLsNr + ')' : ''}`} disabled={saveEnabled} />
110             </Tabs>
111         }
112         <DialogContent>
113             {tabNr === SharingDialogTab.PERMISSIONS &&
114                 <Grid container direction='column' spacing={24}>
115                     <Grid item>
116                         <SharingInvitationForm onSave={onSave} saveEnabled={saveEnabled} />
117                     </Grid>
118                     <Grid item>
119                         <SharingManagementForm onSave={onSave} />
120                     </Grid>
121                     <Grid item>
122                         <SharingPublicAccessForm onSave={onSave} />
123                     </Grid>
124                 </Grid>
125             }
126             {tabNr === SharingDialogTab.URLS &&
127                 <SharingURLsContent uuid={sharedResourceUuid} />
128             }
129         </DialogContent>
130         <DialogActions>
131             <Grid container spacing={8}>
132                 {tabNr === SharingDialogTab.URLS && withExpiration && <>
133                     <Grid item container direction='row' md={12}>
134                         <MuiPickersUtilsProvider utils={DateFnsUtils}>
135                             <BasePicker autoOk value={expDate} onChange={setExpDate}>
136                                 {({ date, handleChange }) => (<>
137                                     <Grid item md={6}>
138                                         <Calendar date={date} minDate={new Date()} maxDate={undefined}
139                                             onChange={handleChange} />
140                                     </Grid>
141                                     <Grid item md={6}>
142                                         <TimePickerView type="hours" date={date} ampm={false}
143                                             onMinutesChange={() => { }}
144                                             onSecondsChange={() => { }}
145                                             onHourChange={handleChange}
146                                         />
147                                     </Grid>
148                                 </>)}
149                             </BasePicker>
150                         </MuiPickersUtilsProvider>
151                     </Grid>
152                     <Grid item md={12}>
153                         <Typography variant='caption' align='center'>
154                             Maximum expiration date may be limited by the cluster configuration.
155                         </Typography>
156                     </Grid>
157                 </>
158                 }
159                 {tabNr === SharingDialogTab.PERMISSIONS && !sharingURLsDisabled &&
160                     privateAccess && sharingURLsNr > 0 &&
161                     <Grid item md={12}>
162                         <Typography variant='caption' align='center' color='error'>
163                             Although there aren't specific permissions set, this is publicly accessible via Sharing URL(s).
164                         </Typography>
165                     </Grid>
166                 }
167                 <Grid item xs />
168                 {tabNr === SharingDialogTab.URLS && <>
169                     <Grid item><FormControlLabel
170                         control={<Checkbox color="primary" checked={withExpiration}
171                             onChange={(e) => setWithExpiration(e.target.checked)} />}
172                         label="With expiration" />
173                     </Grid>
174                     <Grid item>
175                         <Button variant="contained" color="primary"
176                             disabled={expDate !== undefined && expDate <= new Date()}
177                             onClick={onCreateSharingToken(expDate)}>
178                             Create sharing URL
179                         </Button>
180                     </Grid>
181                 </>
182                 }
183                 <Grid item>
184                     <Button onClick={() => {
185                         onClose();
186                         setWithExpiration(false);
187                     }}>
188                         Close
189                     </Button>
190                 </Grid>
191             </Grid>
192         </DialogActions>
193         {
194             loading && <LoadingIndicator />
195         }
196     </Dialog>;
197 };
198
199 const loadingIndicatorStyles: StyleRulesCallback<'root'> = theme => ({
200     root: {
201         position: 'absolute',
202         top: 0,
203         right: 0,
204         bottom: 0,
205         left: 0,
206         display: 'flex',
207         alignItems: 'center',
208         justifyContent: 'center',
209         backgroundColor: 'rgba(255, 255, 255, 0.8)',
210     },
211 });
212
213 const LoadingIndicator = withStyles(loadingIndicatorStyles)(
214     (props: WithStyles<'root'>) =>
215         <Paper classes={props.classes}>
216             <CircularProgress />
217         </Paper>
218 );