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