Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions frontend/src/actions/dataset.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,40 @@ import {handleErrors} from "./common";
import config from "../app.config";
import {getHeader} from "../utils/common";

export const SET_DATASET_GROUP_ROLE = "SET_DATASET_GROUP_ROLE";
export function setDatasetGroupRole(datasetId, groupId, roleType){
return (dispatch) => {
return V2.AuthorizationService.setDatasetGroupRoleApiV2AuthorizationsDatasetsDatasetIdGroupRoleGroupIdRolePost(datasetId, groupId, roleType)
.then(json => {
dispatch({
type: SET_DATASET_GROUP_ROLE,
files: json,
receivedAt: Date.now(),
});
})
.catch(reason => {
dispatch(handleErrors(reason, setDatasetGroupRole(datasetId, groupId, roleType)));
});
};
}

export const SET_DATASET_USER_ROLE = "SET_DATASET_USER_ROLE";
export function setDatasetUserRole(datasetId, username, roleType){
return (dispatch) => {
return V2.AuthorizationService.setDatasetUserRoleApiV2AuthorizationsDatasetsDatasetIdUserRoleUsernameRolePost(datasetId, username, roleType)
.then(json => {
dispatch({
type: SET_DATASET_USER_ROLE,
files: json,
receivedAt: Date.now(),
});
})
.catch(reason => {
dispatch(handleErrors(reason, setDatasetUserRole(datasetId, username, roleType)));
});
};
}

export const RECEIVE_FILES_IN_DATASET = "RECEIVE_FILES_IN_DATASET";
export function fetchFilesInDataset(datasetId, folderId){
return (dispatch) => {
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/components/datasets/ActionsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import {AuthWrapper} from "../auth/AuthWrapper";

type ActionsMenuProps = {
datasetId: string,
folderId: string
folderId: string,
datasetName: string
}

export const ActionsMenu = (props: ActionsMenuProps): JSX.Element => {
const {datasetId, folderId} = props;
const {datasetId, folderId, datasetName} = props;

const datasetRole = useSelector((state: RootState) => state.dataset.datasetRole);

Expand Down Expand Up @@ -53,7 +54,7 @@ export const ActionsMenu = (props: ActionsMenuProps): JSX.Element => {
{/*owner can delete and perform other tasks*/}
{
<AuthWrapper currRole={datasetRole.role} allowedRoles={["owner"]}>
<OtherMenu datasetId={datasetId} folderId={folderId}/>
<OtherMenu datasetId={datasetId} folderId={folderId} datasetName={datasetName}/>
</AuthWrapper>
}
</Stack>)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/datasets/Dataset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export const Dataset = (): JSX.Element => {
</Grid>
{/*actions*/}
<Grid item xs={4} sx={{display: "flex", alignItems: "center"}}>
<ActionsMenu datasetId={datasetId} folderId={folderId}/>
<ActionsMenu datasetId={datasetId} folderId={folderId} datasetName={about["name"]}/>
</Grid>
</Grid>
<Grid container spacing={2}>
Expand Down
58 changes: 54 additions & 4 deletions frontend/src/components/datasets/OtherMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
import {Box, Button, ListItemIcon, ListItemText, Menu, MenuItem} from "@mui/material";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import React, {useState} from "react";
import React, {useEffect, useState} from "react";
import {ActionModal} from "../dialog/ActionModal";
import {datasetDeleted} from "../../actions/dataset";
import {datasetDeleted, fetchFilesInDataset} from "../../actions/dataset";
import {fetchGroups} from "../../actions/group";
import {useNavigate} from "react-router-dom";
import {useDispatch} from "react-redux";
import {MoreHoriz} from "@material-ui/icons";
import DeleteIcon from "@mui/icons-material/Delete";
import ShareIcon from '@mui/icons-material/Share';
import ShareDatasetModal from "./ShareDatasetModal"
import ShareGroupDatasetModal from "./ShareGroupDatasetModal";

type ActionsMenuProps = {
datasetId: string
datasetId: string,
datasetName: string
}

export const OtherMenu = (props: ActionsMenuProps): JSX.Element => {
const {datasetId} = props;
const {datasetId, datasetName} = props;

// use history hook to redirect/navigate between routes
const history = useNavigate();

// redux
const dispatch = useDispatch();
const deleteDataset = (datasetId: string | undefined) => dispatch(datasetDeleted(datasetId));
const listGroups = () => dispatch(fetchGroups(0, 21));

// component did mount
useEffect(() => {
listGroups();
}, []);

// state
const [deleteDatasetConfirmOpen, setDeleteDatasetConfirmOpen] = useState(false);
const [sharePaneOpen, setSharePaneOpen] = useState(false);
const [shareGroupPaneOpen, setShareGroupPaneOpen] = useState(false);



// delete dataset
const deleteSelectedDataset = () => {
Expand All @@ -45,6 +60,14 @@ export const OtherMenu = (props: ActionsMenuProps): JSX.Element => {
setAnchorEl(null);
};

const handleShareClose = () => {
setSharePaneOpen(false);
}

const handleShareGroupClose = () => {
setShareGroupPaneOpen(false);
}

return (
<Box>
<ActionModal actionOpen={deleteDatasetConfirmOpen} actionTitle="Are you sure?"
Expand All @@ -53,6 +76,11 @@ export const OtherMenu = (props: ActionsMenuProps): JSX.Element => {
handleActionCancel={() => {
setDeleteDatasetConfirmOpen(false);
}}/>

<ShareDatasetModal open={sharePaneOpen} handleClose={handleShareClose} datasetName={datasetName}/>
<ShareGroupDatasetModal open={shareGroupPaneOpen} handleClose={handleShareGroupClose} datasetName={datasetName}/>


<Button variant="outlined" onClick={handleOptionClick}
endIcon={<ArrowDropDownIcon/>}>
<MoreHoriz/>
Expand All @@ -74,6 +102,28 @@ export const OtherMenu = (props: ActionsMenuProps): JSX.Element => {
<DeleteIcon fontSize="small"/>
</ListItemIcon>
<ListItemText>Delete Dataset</ListItemText></MenuItem>
<MenuItem
onClick={() => {
handleOptionClose();
setSharePaneOpen(true);
}
}>
<ListItemIcon>
<ShareIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Share</ListItemText>
</MenuItem>
<MenuItem
onClick={() => {
handleOptionClose();
setShareGroupPaneOpen(true);
}
}>
<ListItemIcon>
<ShareIcon fontSize="small" />
</ListItemIcon>
<ListItemText>Share With Group</ListItemText>
</MenuItem>
</Menu>
</Box>)
}
113 changes: 113 additions & 0 deletions frontend/src/components/datasets/ShareDatasetModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React, { useEffect, useState } from "react";
import { Alert, Autocomplete, Button, Collapse, Container, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControl, IconButton, InputLabel, MenuItem, Select, TextField, Typography } from "@mui/material";
import {useParams} from "react-router-dom";
import { setDatasetUserRole } from "../../actions/dataset";
import { useDispatch } from "react-redux";
import CloseIcon from "@mui/icons-material/Close";


type ShareDatasetModalProps = {
open: boolean,
handleClose: any,
datasetName: string
}

export default function ShareDatasetModal(props: ShareDatasetModalProps) {
const dispatch = useDispatch();

const { open, handleClose, datasetName } = props;
const {datasetId} = useParams<{ datasetId?: string }>();
const [email, setEmail] = useState("");
const [role, setRole] = useState("viewer");
const [showSuccessAlert, setShowSuccessAlert] = useState(false);

const setUserRole = (datasetId: string, username: string, role: string) => dispatch(setDatasetUserRole(datasetId, username, role));

// component did mount
useEffect(() => {
// listUsers();
}, []);

const onShare = () => {
setUserRole(datasetId, email, role);
setEmail("");
setRole("viewer");
setShowSuccessAlert(true);
};

return (
<Container>
<Dialog open={open} onClose={handleClose} fullWidth={true} maxWidth="md"
sx={{
".MuiPaper-root": {
padding: "2em",
},
}}>
<DialogTitle>Share dataset &apos;{datasetName}&apos;</DialogTitle>
<Divider />
<DialogContent>
<Typography>Invite people to collaborate</Typography>
<div style={{
display: "flex",
alignItems: "center"
}}>
<Autocomplete
id="email-auto-complete"
freeSolo
autoHighlight
inputValue={email}
onInputChange={(event, value) => {
setEmail(value);
}}
options={["[email protected]"]}
renderInput={(params) => <TextField {...params} sx={{ mt: 1, mr: 1, "alignItems": "right", "width": "450px" }} required label="Enter email address" />}
/> as
<FormControl variant="outlined" sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-label">Status</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={role}
defaultValue={"viewer"}
label="Status"
onChange={(event, value) => {
setRole(event.target.value);
}}
>
<MenuItem value="owner">Owner</MenuItem>
<MenuItem value="editor">Editor</MenuItem>
<MenuItem value="uploader">Uploader</MenuItem>
<MenuItem value="viewer">Viewer</MenuItem>
</Select>
</FormControl>
</div>
<Button variant="contained" sx={{ marginTop: 1 }} onClick={onShare} disabled={(email.length > 0) ? false : true}>Share</Button>
<Collapse in={showSuccessAlert}>
<br/>
<Alert
severity="success"
action={
<IconButton
aria-label="close"
color="inherit"
size="small"
onClick={() => {
setShowSuccessAlert(false);
}}
>
<CloseIcon fontSize="inherit" />
</IconButton>
}
sx={{ mb: 2 }}
>
Successfully added role!
</Alert>
</Collapse>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Close</Button>
</DialogActions>
</Dialog>
</Container>
);
}
Loading