1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React from 'react';
19 } from '@mui/material';
20 import { CustomStyleRulesCallback } from 'common/custom-theme';
21 import { WithStyles } from '@mui/styles';
22 import withStyles from '@mui/styles/withStyles';
23 import { DialogActions } from 'components/dialog-actions/dialog-actions';
24 import { SharingURLsContent } from './sharing-urls';
26 extractUuidObjectType,
28 } from 'models/resource';
29 import { SharingInvitationForm } from './sharing-invitation-form';
30 import { SharingManagementForm } from './sharing-management-form';
31 import moment, { Moment } from 'moment';
32 import { SharingPublicAccessForm } from './sharing-public-access-form';
33 import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
34 import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment';
35 import { StaticDateTimePicker } from '@mui/x-date-pickers/StaticDateTimePicker';
37 export interface SharingDialogDataProps {
41 sharedResourceUuid: string;
42 sharingURLsNr: number;
43 privateAccess: boolean;
44 sharingURLsDisabled: boolean;
47 export interface SharingDialogActionProps {
50 onCreateSharingToken: (d: Date | undefined) => () => void;
51 refreshPermissions: () => void;
53 enum SharingDialogTab {
57 export type SharingDialogComponentProps = SharingDialogDataProps & SharingDialogActionProps;
59 export const SharingDialogComponent = (props: SharingDialogComponentProps) => {
60 const { open, loading, saveEnabled, sharedResourceUuid,
61 sharingURLsNr, privateAccess, sharingURLsDisabled,
62 onClose, onSave, onCreateSharingToken, refreshPermissions } = props;
63 const showTabs = !sharingURLsDisabled && extractUuidObjectType(sharedResourceUuid) === ResourceObjectType.COLLECTION;
64 const [tabNr, setTabNr] = React.useState<number>(SharingDialogTab.PERMISSIONS);
65 const [expDate, setExpDate] = React.useState<Moment>();
66 const [withExpiration, setWithExpiration] = React.useState<boolean>(false);
68 const handleChange = (newValue: moment.Moment) => setExpDate(newValue);
69 const handleClose = (ev, reason) => {
70 if (reason !== 'backdropClick') {
75 // Sets up the dialog depending on the resource type
76 if (!showTabs && tabNr !== SharingDialogTab.PERMISSIONS) {
77 setTabNr(SharingDialogTab.PERMISSIONS);
80 React.useEffect(() => {
81 if (!withExpiration) {
82 setExpDate(undefined);
84 setExpDate(moment().add(2, 'hour'));
89 <Dialog {...{ open, onClose }} className="sharing-dialog" onClose={handleClose} fullWidth maxWidth='sm' >
95 onChange={(_, tb) => {
96 if (tb === SharingDialogTab.PERMISSIONS) {
102 <Tab label="With users/groups" />
103 <Tab label={`Sharing URLs ${sharingURLsNr > 0 ? '(' + sharingURLsNr + ')' : ''}`} disabled={saveEnabled} />
107 {tabNr === SharingDialogTab.PERMISSIONS &&
108 <Grid container direction='column' spacing={3}>
110 <SharingInvitationForm onSave={onSave} />
113 <SharingManagementForm onSave={onSave} />
116 <SharingPublicAccessForm onSave={onSave} />
120 {tabNr === SharingDialogTab.URLS &&
121 <SharingURLsContent uuid={sharedResourceUuid} />
125 <Grid container spacing={1} style={{ display: 'flex', width: '100%', flexDirection: 'column', alignItems: 'center'}}>
126 {tabNr === SharingDialogTab.URLS && withExpiration &&
128 <section style={{minHeight: '42dvh', display: 'flex', flexDirection: 'column' }}>
129 <LocalizationProvider dateAdapter={AdapterMoment}>
130 <StaticDateTimePicker
131 orientation="landscape"
132 onChange={handleChange}
133 value={expDate || moment().add(2, 'hour')}
138 //removes redundant action bar
139 actionBar: () => null,
142 </LocalizationProvider>
144 <Typography variant='caption' align='center' marginBottom='1rem'>
145 Maximum expiration date may be limited by the cluster configuration.
149 {tabNr === SharingDialogTab.PERMISSIONS && !sharingURLsDisabled &&
150 privateAccess && sharingURLsNr > 0 &&
152 <Typography variant='caption' align='center' color='error'>
153 Although there aren't specific permissions set, this is publicly accessible via Sharing URL(s).
157 <Grid style={{display: 'flex', justifyContent: 'end', flexDirection: 'row', width: '100%', marginBottom: '-0.5rem'}}>
158 {tabNr === SharingDialogTab.URLS &&
159 <Grid container style={{ display: 'flex', justifyContent: 'space-between'}}>
160 <Grid display='flex'>
163 control={<Checkbox color="primary" checked={withExpiration}
164 onChange={(e) => setWithExpiration(e.target.checked)} />}
165 label="With expiration" />
168 <Button variant="contained" color="primary"
169 disabled={expDate !== undefined && expDate.toDate() <= new Date()}
170 onClick={onCreateSharingToken(expDate?.toDate())}>
178 <Grid style={{display: 'flex'}}>
179 <Button onClick={() => {
181 setWithExpiration(false);
183 disabled={saveEnabled}
186 style={{ marginLeft: '10px' }}
190 {tabNr !== SharingDialogTab.URLS &&
191 <Button onClick={() => {
194 data-cy="add-invited-people"
195 disabled={!saveEnabled}
199 style={{ marginLeft: '10px' }}
210 loading && <LoadingIndicator />
216 const loadingIndicatorStyles: CustomStyleRulesCallback<'root'> = theme => ({
218 position: 'absolute',
224 alignItems: 'center',
225 justifyContent: 'center',
226 backgroundColor: 'rgba(255, 255, 255, 0.8)',
230 const LoadingIndicator = withStyles(loadingIndicatorStyles)(
231 (props: WithStyles<'root'>) =>
232 <Paper classes={props.classes}>