diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/AddTagModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/AddTagModal.tsx new file mode 100644 index 000000000..09736419d --- /dev/null +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/AddTagModal.tsx @@ -0,0 +1,138 @@ +import React, { useState } from "react"; +import { Button, Grid } from "@material-ui/core"; +import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; +import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper"; +import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; +import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary"; +import api from "../../../../../../common/api"; + +interface ITagModal { + modalOpen: boolean; + currentTags: any; + bucketName: string; + versionId: string; + onCloseAndUpdate: (refresh: boolean) => void; + selectedObject: string; + classes: any; +} + +const styles = (theme: Theme) => + createStyles({ + buttonContainer: { + textAlign: "right", + }, + pathLabel: { + marginTop: 0, + marginBottom: 32, + }, + ...modalBasic, + }); + +const AddTagModal = ({ + modalOpen, + currentTags, + selectedObject, + onCloseAndUpdate, + bucketName, + versionId, + classes, +}: ITagModal) => { + const [newKey, setNewKey] = useState(""); + const [newLabel, setNewLabel] = useState(""); + const [error, setError] = useState(""); + const [isSending, setIsSending] = useState(false); + + const resetForm = () => { + setNewLabel(""); + setNewKey(""); + }; + + const addTagProcess = () => { + setIsSending(true); + const newTag: any = {}; + + newTag[newKey] = newLabel; + const newTagList = { ...currentTags, ...newTag }; + + api + .invoke( + "PUT", + `/api/v1/buckets/${bucketName}/objects/tags?prefix=${selectedObject}&version_id=${versionId}`, + { tags: newTagList } + ) + .then((res: any) => { + setIsSending(false); + onCloseAndUpdate(true); + }) + .catch((error) => { + setError(error); + setIsSending(false); + }); + }; + + return ( + + { + onCloseAndUpdate(false); + }} + > + +

+ Selected Object: {selectedObject} +

+ {error !== "" && {error}} + + { + setNewKey(e.target.value); + }} + /> + + + { + setNewLabel(e.target.value); + }} + /> + + + + + +
+
+
+ ); +}; + +export default withStyles(styles)(AddTagModal); diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/DeleteTagModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/DeleteTagModal.tsx new file mode 100644 index 000000000..ca11a96d6 --- /dev/null +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/DeleteTagModal.tsx @@ -0,0 +1,125 @@ +import React, { useState } from "react"; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Grid, + LinearProgress, +} from "@material-ui/core"; +import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; +import { modalBasic } from "../../../../Common/FormComponents/common/styleLibrary"; +import api from "../../../../../../common/api"; +import Typography from "@material-ui/core/Typography"; + +interface IDeleteTagModal { + deleteOpen: boolean; + currentTags: any; + bucketName: string; + versionId: string; + selectedTag: [string, string]; + onCloseAndUpdate: (refresh: boolean) => void; + selectedObject: string; + classes: any; +} + +const styles = (theme: Theme) => + createStyles({ + buttonContainer: { + textAlign: "right", + }, + pathLabel: { + marginTop: 0, + marginBottom: 32, + }, + ...modalBasic, + }); + +const DeleteTagModal = ({ + deleteOpen, + currentTags, + selectedObject, + selectedTag, + onCloseAndUpdate, + bucketName, + versionId, + classes, +}: IDeleteTagModal) => { + const [deleteError, setDeleteError] = useState(""); + const [deleteLoading, setDeleteSending] = useState(false); + const [tagKey, tagLabel] = selectedTag; + + const removeTagProcess = () => { + setDeleteSending(true); + const cleanObject = { ...currentTags }; + delete cleanObject[tagKey]; + + api + .invoke( + "PUT", + `/api/v1/buckets/${bucketName}/objects/tags?prefix=${selectedObject}&version_id=${versionId}`, + { tags: cleanObject } + ) + .then((res: any) => { + setDeleteSending(false); + onCloseAndUpdate(true); + }) + .catch((error) => { + setDeleteError(error); + setDeleteSending(false); + }); + }; + + return ( + { + onCloseAndUpdate(false); + }} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + Delete Tag + + {deleteLoading && } + + Are you sure you want to delete the tag{" "} + + {tagKey} : {tagLabel} + {" "} + from {selectedObject}? + {deleteError !== "" && ( + +
+ + {deleteError} + +
+ )} +
+
+ + + + +
+ ); +}; + +export default withStyles(styles)(DeleteTagModal); diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails.tsx index b555f7f89..d56f8e4ab 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ObjectDetails.tsx @@ -48,6 +48,8 @@ import BrowserBreadcrumbs from "../../../../ObjectBrowser/BrowserBreadcrumbs"; import DeleteObject from "../ListObjects/DeleteObject"; import { removeRouteLevel } from "../../../../ObjectBrowser/actions"; import { connect } from "react-redux"; +import AddTagModal from "./AddTagModal"; +import DeleteTagModal from "./DeleteTagModal"; const styles = (theme: Theme) => createStyles({ @@ -163,8 +165,12 @@ const ObjectDetails = ({ routesList, removeRouteLevel, }: IObjectDetailsProps) => { + const [loadObjectData, setLoadObjectData] = useState(true); const [shareFileModalOpen, setShareFileModalOpen] = useState(false); const [retentionModalOpen, setRetentionModalOpen] = useState(false); + const [tagModalOpen, setTagModalOpen] = useState(false); + const [deleteTagModalOpen, setDeleteTagModalOpen] = useState(false); + const [selectedTag, setSelectedTag] = useState<[string, string]>(["", ""]); const [actualInfo, setActualInfo] = useState(emptyFile); const [versions, setVersions] = useState([]); const [filterVersion, setFilterVersion] = useState(""); @@ -178,22 +184,32 @@ const ObjectDetails = ({ const pathInBucket = allPathData.slice(3).join("/"); useEffect(() => { - api - .invoke( - "GET", - `/api/v1/buckets/${bucketName}/objects?prefix=${pathInBucket}&with_versions=true` - ) - .then((res: IFileInfo[]) => { - const result = get(res, "objects", []); - setActualInfo( - result.find((el: IFileInfo) => el.is_latest) || emptyFile - ); - setVersions(result.filter((el: IFileInfo) => !el.is_latest)); - }) - .catch((error) => { - setError(error); - }); - }, []); + if (loadObjectData) { + api + .invoke( + "GET", + `/api/v1/buckets/${bucketName}/objects?prefix=${pathInBucket}&with_versions=true` + ) + .then((res: IFileInfo[]) => { + const result = get(res, "objects", []); + setActualInfo( + result.find((el: IFileInfo) => el.is_latest) || emptyFile + ); + setVersions(result.filter((el: IFileInfo) => !el.is_latest)); + setLoadObjectData(false); + }) + .catch((error) => { + setError(error); + setLoadObjectData(false); + }); + } + }, [loadObjectData]); + + let tagKeys: string[] = []; + + if (actualInfo.tags) { + tagKeys = Object.keys(actualInfo.tags); + } const openRetentionModal = () => { setRetentionModalOpen(true); @@ -215,8 +231,9 @@ const ObjectDetails = ({ console.log("close share modal"); }; - const deleteTag = () => { - console.log("delete tag"); + const deleteTag = (tagKey: string, tagLabel: string) => { + setSelectedTag([tagKey, tagLabel]); + setDeleteTagModalOpen(true); }; const downloadObject = (path: string) => { @@ -247,6 +264,22 @@ const ObjectDetails = ({ } }; + const closeAddTagModal = (reloadObjectData: boolean) => { + setTagModalOpen(false); + + if (reloadObjectData) { + setLoadObjectData(true); + } + }; + + const closeDeleteTagModal = (reloadObjectData: boolean) => { + setDeleteTagModalOpen(false); + + if (reloadObjectData) { + setLoadObjectData(true); + } + }; + return ( @@ -271,6 +304,27 @@ const ObjectDetails = ({ closeDeleteModalAndRefresh={closeDeleteModal} /> )} + {tagModalOpen && ( + + )} + {deleteTagModalOpen && ( + + )} @@ -371,24 +425,25 @@ const ObjectDetails = ({
Tags:
- {actualInfo.tags && - Object.keys(actualInfo.tags).map((itemKey, index) => { - const tag = get(actualInfo, `tags.${itemKey}`, ""); - if (tag !== "") { - return ( - } - onDelete={deleteTag} - /> - ); - } - return null; - })} + {tagKeys.map((tagKey, index) => { + const tag = get(actualInfo, `tags.${tagKey}`, ""); + if (tag !== "") { + return ( + } + onDelete={() => { + deleteTag(tagKey, tag); + }} + /> + ); + } + return null; + })} } @@ -397,6 +452,9 @@ const ObjectDetails = ({ label="Add tag" color="primary" variant="outlined" + onClick={() => { + setTagModalOpen(true); + }} />