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