diff --git a/frontend/src/actions/dataset.js b/frontend/src/actions/dataset.js index 7fef97116..ba59f1e18 100644 --- a/frontend/src/actions/dataset.js +++ b/frontend/src/actions/dataset.js @@ -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) => { diff --git a/frontend/src/components/datasets/ActionsMenu.tsx b/frontend/src/components/datasets/ActionsMenu.tsx index f206a0327..972705606 100644 --- a/frontend/src/components/datasets/ActionsMenu.tsx +++ b/frontend/src/components/datasets/ActionsMenu.tsx @@ -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); @@ -53,7 +54,7 @@ export const ActionsMenu = (props: ActionsMenuProps): JSX.Element => { {/*owner can delete and perform other tasks*/} { - + } ) diff --git a/frontend/src/components/datasets/Dataset.tsx b/frontend/src/components/datasets/Dataset.tsx index 28f230d58..51b832ef5 100644 --- a/frontend/src/components/datasets/Dataset.tsx +++ b/frontend/src/components/datasets/Dataset.tsx @@ -182,7 +182,7 @@ export const Dataset = (): JSX.Element => { {/*actions*/} - + diff --git a/frontend/src/components/datasets/OtherMenu.tsx b/frontend/src/components/datasets/OtherMenu.tsx index 1adc057e9..fba154a6f 100644 --- a/frontend/src/components/datasets/OtherMenu.tsx +++ b/frontend/src/components/datasets/OtherMenu.tsx @@ -1,19 +1,24 @@ 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(); @@ -21,9 +26,19 @@ export const OtherMenu = (props: ActionsMenuProps): JSX.Element => { // 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 = () => { @@ -45,6 +60,14 @@ export const OtherMenu = (props: ActionsMenuProps): JSX.Element => { setAnchorEl(null); }; + const handleShareClose = () => { + setSharePaneOpen(false); + } + + const handleShareGroupClose = () => { + setShareGroupPaneOpen(false); + } + return ( { handleActionCancel={() => { setDeleteDatasetConfirmOpen(false); }}/> + + + + + + +
+ { + setShowSuccessAlert(false); + }} + > + + + } + sx={{ mb: 2 }} + > + Successfully added role! + +
+ + + + + + + ); +} diff --git a/frontend/src/components/datasets/ShareGroupDatasetModal.tsx b/frontend/src/components/datasets/ShareGroupDatasetModal.tsx new file mode 100644 index 000000000..b3ebedfac --- /dev/null +++ b/frontend/src/components/datasets/ShareGroupDatasetModal.tsx @@ -0,0 +1,142 @@ +import React, {useEffect, useState} from "react"; + +import {useDispatch, useSelector} from "react-redux"; +import { Alert, Autocomplete, Button, Collapse, Container, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControl, IconButton, InputLabel, MenuItem, Select, TextField, Typography } from "@mui/material"; +import {fetchGroups} from "../../actions/group"; +import {RootState} from "../../types/data"; +import {setDatasetGroupRole} from "../../actions/dataset"; +import {useParams} from "react-router-dom"; +import CloseIcon from "@mui/icons-material/Close"; + +type ShareGroupDatasetModalProps = { + open: boolean, + handleClose: any, + datasetName: string +} + +export default function ShareGroupDatasetModal(props: ShareGroupDatasetModalProps) { + const { open, handleClose, datasetName } = props; + const {datasetId} = useParams<{ datasetId?: string }>(); + const [role, setRole] = useState("viewer"); + const [group, setGroup] = useState(""); + const [showSuccessAlert, setShowSuccessAlert] = useState(false); + const dispatch = useDispatch(); + const listGroups = () => dispatch(fetchGroups(0, 21)); + const groups = useSelector((state: RootState) => state.group.groups); + const setGroupRole = (datasetId: string , groupId: string, role: string) => dispatch(setDatasetGroupRole(datasetId, groupId, role)); + + + // component did mount + useEffect(() => { + listGroups(); + }, []); + + const onShare = () => { + console.log(group, datasetId,role); + setGroupRole(datasetId, group, role); + setGroup(""); + setRole("viewer"); + setShowSuccessAlert(true); + }; + + + + const options = Array(); + groups.map((group) => { + const group_option = {value:group.id, label:group.name}; + options.push(group_option); + }); + console.log("group optioins are", options); + console.log("it is of type", typeof(options)); + + return ( + + + Share dataset '{datasetName}' + + + Invite groups to collaborate +
+ + Group + + + + Status + + +
+ + +
+ { + setShowSuccessAlert(false); + }} + > + + + } + sx={{ mb: 2 }} + > + Successfully added role! + +
+
+ + + +
+
+ ); +} diff --git a/frontend/src/reducers/dataset.ts b/frontend/src/reducers/dataset.ts index 86b86fd1b..d2961961e 100644 --- a/frontend/src/reducers/dataset.ts +++ b/frontend/src/reducers/dataset.ts @@ -6,7 +6,9 @@ import { DELETE_DATASET, CREATE_DATASET, RESET_CREATE_DATASET, - DOWNLOAD_DATASET + DOWNLOAD_DATASET, + SET_DATASET_GROUP_ROLE, + SET_DATASET_USER_ROLE } from "../actions/dataset"; import {CREATE_FILE, UPDATE_FILE, DELETE_FILE, RESET_CREATE_FILE} from "../actions/file"; import {RECEIVE_DATASET_ROLE} from "../actions/authorization"; @@ -42,6 +44,10 @@ const dataset = (state = defaultState, action: DataAction) => { }); case RESET_CREATE_FILE: return Object.assign({}, state, {newFile: {}}) + case SET_DATASET_GROUP_ROLE: + return Object.assign({}, state, {}) + case SET_DATASET_USER_ROLE: + return Object.assign({}, state, {}) case UPDATE_FILE: return Object.assign({}, state, { files: state.files.map(file => file.id === action.file.id ? action.file: file),