diff --git a/web-app/package.json b/web-app/package.json index 5217f9ad45..0717354972 100644 --- a/web-app/package.json +++ b/web-app/package.json @@ -5,6 +5,7 @@ "homepage": ".", "private": true, "dependencies": { + "@emotion/react": "^11.14.0", "@reduxjs/toolkit": "^1.9.7", "clsx": "^2.1.1", "http-status-codes": "^2.3.0", @@ -12,7 +13,7 @@ "local-storage-fallback": "^4.1.2", "lodash": "^4.17.21", "luxon": "^3.4.4", - "mds": "https://github.com/minio/mds.git#v1.0.4", + "mds": "https://github.com/minio/mds.git#v3.1.9", "react": "^18.3.1", "react-component-export-image": "^1.0.6", "react-copy-to-clipboard": "^5.1.0", @@ -26,7 +27,6 @@ "react-window": "^1.8.10", "react-window-infinite-loader": "^1.0.9", "recharts": "^2.12.7", - "styled-components": "5.3.11", "superagent": "^9.0.2", "tinycolor2": "^1.6.0" }, @@ -99,5 +99,5 @@ "cookie": "^0.7.2" }, "main": "index.js", - "packageManager": "yarn@4.4.0" + "packageManager": "yarn@4.5.3" } diff --git a/web-app/src/StyleHandler.tsx b/web-app/src/StyleHandler.tsx index 90e0e84aed..9439fb0844 100644 --- a/web-app/src/StyleHandler.tsx +++ b/web-app/src/StyleHandler.tsx @@ -18,7 +18,6 @@ import React, { Fragment } from "react"; import { GlobalStyles, ThemeHandler } from "mds"; import "react-virtualized/styles.css"; -import { generateOverrideTheme } from "./utils/stylesUtils"; import "./index.css"; import { useSelector } from "react-redux"; import { AppState } from "./store"; @@ -33,16 +32,16 @@ const StyleHandler = ({ children }: IStyleHandler) => { ); const darkMode = useSelector((state: AppState) => state.system.darkMode); - let thm = undefined; + /*let thm = undefined; if (colorVariants) { thm = generateOverrideTheme(colorVariants); - } + }*/ return ( - - + + {children} diff --git a/web-app/src/common/MoreLink.tsx b/web-app/src/common/MoreLink.tsx index 2ed53b9393..af39e33744 100644 --- a/web-app/src/common/MoreLink.tsx +++ b/web-app/src/common/MoreLink.tsx @@ -15,7 +15,7 @@ // along with this program. If not, see . import React from "react"; -import { ArrowIcon, Box } from "mds"; +import { ArrowRightIcon, Box } from "mds"; const MoreLink = ({ LeadingIcon, @@ -74,7 +74,7 @@ const MoreLink = ({ marginTop: 2, }} > - + diff --git a/web-app/src/screens/Console/Dashboard/dashboardThunks.ts b/web-app/src/common/MultipleBase.tsx similarity index 55% rename from web-app/src/screens/Console/Dashboard/dashboardThunks.ts rename to web-app/src/common/MultipleBase.tsx index cff2a7d84b..10d27f9c4a 100644 --- a/web-app/src/screens/Console/Dashboard/dashboardThunks.ts +++ b/web-app/src/common/MultipleBase.tsx @@ -14,22 +14,31 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import { createAsyncThunk } from "@reduxjs/toolkit"; -import { setErrorSnackMessage } from "../../../systemSlice"; -import { api } from "api"; -import { errorToHandler } from "api/errors"; +import { styled } from "mds"; -export const getUsageAsync = createAsyncThunk( - "dashboard/getUsageAsync", - async (_, { getState, rejectWithValue, dispatch }) => { - return api.admin - .adminInfo() - .then((res) => { - return res.data; - }) - .catch((err) => { - dispatch(setErrorSnackMessage(errorToHandler(err.error))); - return rejectWithValue(err); - }); +const MultipleBase = styled.div(({ theme }) => ({ + display: "flex", + alignItems: "center", + justifyContent: "space-between", + padding: 4, + borderRadius: 12, + backgroundColor: theme.colors["Color/Neutral/Bg/colorBgShell"], + "& #expand-options .button-label": { + display: "none", }, -); + "& .selectionElm": { + fontSize: 12, + fontStyle: "normal", + fontWeight: 600, + lineHeight: "16px", + letterSpacing: "0.5px", + }, + "& .detSelection": { + display: "flex", + alignItems: "center", + gap: 24, + padding: "0 4px", + }, +})); + +export default MultipleBase; diff --git a/web-app/src/common/utils.ts b/web-app/src/common/utils.ts index 6f37551083..0b8742acdf 100644 --- a/web-app/src/common/utils.ts +++ b/web-app/src/common/utils.ts @@ -523,3 +523,15 @@ export const safeDecodeURIComponent = (value: any) => { return value; } }; + +// calculate last modify time +export const calculateLastModifyTime = (lastModified: string) => { + const currentTime = new Date(); + const modifiedTime = new Date(lastModified); + + const difTime = currentTime.getTime() - modifiedTime.getTime(); + + const formatTime = niceDaysInt(difTime, "ms"); + + return formatTime.trim() !== "" ? `${formatTime} ago` : "Just now"; +}; diff --git a/web-app/src/screens/AnonymousAccess/AnonymousAccess.tsx b/web-app/src/screens/AnonymousAccess/AnonymousAccess.tsx index f4321fa13b..a60ea9a6b7 100644 --- a/web-app/src/screens/AnonymousAccess/AnonymousAccess.tsx +++ b/web-app/src/screens/AnonymousAccess/AnonymousAccess.tsx @@ -55,7 +55,7 @@ const AnonymousAccess = () => {
- - + + ); }; diff --git a/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsHelpers.tsx b/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsHelpers.tsx index ab675fd2a3..458d111fc0 100644 --- a/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsHelpers.tsx +++ b/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsHelpers.tsx @@ -18,16 +18,18 @@ import { DateTime } from "luxon"; import { BucketObjectItem } from "./types"; import { niceBytes } from "../../../../../../common/utils"; import { displayFileIconName } from "./utils"; +import { IColumns } from "mds"; +import { BucketObject } from "../../../../../../api/consoleApi"; // Functions -const displayParsedDate = (object: BucketObjectItem) => { - if (object.name.endsWith("/")) { +const displayParsedDate = (object: BucketObject) => { + if (!object.name || object.name.endsWith("/")) { return ""; } const currTime = DateTime.now(); - const objectTime = DateTime.fromISO(object.last_modified); + const objectTime = DateTime.fromISO(object.last_modified || ""); const isToday = currTime.hasSame(objectTime, "day") && @@ -41,20 +43,20 @@ const displayParsedDate = (object: BucketObjectItem) => { return objectTime.toFormat("ccc, LLL dd yyyy HH:mm (ZZZZ)"); }; -const displayNiceBytes = (object: BucketObjectItem) => { - if (object.name.endsWith("/") || !object.size) { +const displayNiceBytes = (object: BucketObject) => { + if (!object.name || object.name.endsWith("/") || !object.size) { return "-"; } return niceBytes(String(object.size)); }; -const displayDeleteFlag = (state: boolean) => { +const displayDeleteFlag = (state?: boolean) => { return state ? "Yes" : "No"; }; // Table Props -export const listModeColumns = [ +export const listModeColumns: IColumns[] = [ { label: "Name", elementKey: "name", @@ -64,21 +66,21 @@ export const listModeColumns = [ { label: "Last Modified", elementKey: "last_modified", - renderFunction: displayParsedDate, - renderFullObject: true, + renderFullObjectFunction: displayParsedDate, + enableSort: true, }, { label: "Size", elementKey: "size", - renderFunction: displayNiceBytes, - renderFullObject: true, + renderFullObjectFunction: displayNiceBytes, + width: 100, enableSort: true, }, ]; -export const rewindModeColumns = [ +export const rewindModeColumns: IColumns[] = [ { label: "Name", elementKey: "name", @@ -88,21 +90,21 @@ export const rewindModeColumns = [ { label: "Object Date", elementKey: "last_modified", - renderFunction: displayParsedDate, - renderFullObject: true, + renderFullObjectFunction: displayParsedDate, + enableSort: true, }, { label: "Size", elementKey: "size", - renderFunction: displayNiceBytes, - renderFullObject: true, + renderFullObjectFunction: displayNiceBytes, + width: 100, enableSort: true, }, { label: "Deleted", - elementKey: "delete_flag", + elementKey: "is_delete_marker", renderFunction: displayDeleteFlag, width: 60, }, diff --git a/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsTable.tsx b/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsTable.tsx index 1f627db4ff..696c759005 100644 --- a/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsTable.tsx +++ b/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/ListObjectsTable.tsx @@ -36,7 +36,7 @@ import { } from "../../../../../../common/SecureComponent/permissions"; import { hasPermission } from "../../../../../../common/SecureComponent"; import { downloadObject } from "../../../../ObjectBrowser/utils"; -import { DataTable, ItemActions } from "mds"; +import { DataTable } from "mds"; import { BucketObject } from "api/consoleApi"; const ListObjectsTable = () => { @@ -123,14 +123,6 @@ const ListObjectsTable = () => { } dispatch(setSelectedObjectView(idElement)); }; - const tableActions: ItemActions[] = [ - { - type: "view", - tooltip: "View", - onClick: openPath, - sendOnlyId: false, - }, - ]; const sortChange = (sortData: any) => { const newSortDirection = get(sortData, "sortDirection", "DESC"); @@ -168,6 +160,8 @@ const ListObjectsTable = () => { dispatch(setSelectedObjects(elements)); dispatch(setSelectedObjectView(null)); + console.log("sd") + return elements; }; @@ -186,7 +180,7 @@ const ListObjectsTable = () => { "Objects List unavailable. Please review your WebSockets configuration and try again"; } - let customPaperHeight = "calc(100vh - 290px)"; + let customPaperHeight = "calc(100vh - 232px)"; if (obOnly) { customPaperHeight = "calc(100vh - 315px)"; @@ -194,7 +188,13 @@ const ListObjectsTable = () => { return ( void; } const ObjectDetailPanel = ({ internalPaths, bucketName, - versioningInfo, - locking, - onClosePanel, }: IObjectDetailPanelProps) => { const dispatch = useAppDispatch(); const distributedSetup = useSelector(selDistSet); - const versionsMode = useSelector( - (state: AppState) => state.objectBrowser.versionsMode, - ); const selectedVersion = useSelector( - (state: AppState) => state.objectBrowser.selectedVersion, + (state: AppState) => state.objectBrowser.selectedVersion ); const loadingObjectInfo = useSelector( - (state: AppState) => state.objectBrowser.loadingObjectInfo, + (state: AppState) => state.objectBrowser.loadingObjectInfo + ); + const actualInfo = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectInfo + ); + const longFileOpen = useSelector( + (state: AppState) => state.objectBrowser.longFileOpen ); - const [shareFileModalOpen, setShareFileModalOpen] = useState(false); - const [retentionModalOpen, setRetentionModalOpen] = useState(false); - const [tagModalOpen, setTagModalOpen] = useState(false); - const [legalholdOpen, setLegalholdOpen] = useState(false); - const [inspectModalOpen, setInspectModalOpen] = useState(false); - const [actualInfo, setActualInfo] = useState(null); const [allInfoElements, setAllInfoElements] = useState([]); - const [objectToShare, setObjectToShare] = useState(null); const [versions, setVersions] = useState([]); - const [deleteOpen, setDeleteOpen] = useState(false); - const [previewOpen, setPreviewOpen] = useState(false); const [totalVersionsSize, setTotalVersionsSize] = useState(0); - const [longFileOpen, setLongFileOpen] = useState(false); - const [metaData, setMetaData] = useState(null); const [loadMetadata, setLoadingMetadata] = useState(false); const internalPathsDecoded = internalPaths || ""; @@ -147,7 +104,7 @@ const ObjectDetailPanel = ({ if (selectedVersion !== "") { infoElement = allInfoElements.find( - (el: BucketObject) => el.version_id === selectedVersion, + (el: BucketObject) => el.version_id === selectedVersion ) || emptyFile; } @@ -155,9 +112,9 @@ const ObjectDetailPanel = ({ setLoadingMetadata(true); } - setActualInfo(infoElement); + dispatch(setObjectInfo(infoElement)); } - }, [selectedVersion, distributedSetup, allInfoElements]); + }, [selectedVersion, distributedSetup, allInfoElements, dispatch]); useEffect(() => { if (loadingObjectInfo && internalPaths !== "") { @@ -179,14 +136,14 @@ const ObjectDetailPanel = ({ } return acc; }, - 0, + 0 ); setTotalVersionsSize(tVersionSize); } else { const resInfo = result[0]; - setActualInfo(resInfo); + dispatch(setObjectInfo(resInfo)); setVersions([]); if (!resInfo.is_delete_marker) { @@ -220,7 +177,7 @@ const ObjectDetailPanel = ({ .then((res) => { let metadata = get(res.data, "objectMetadata", {}); - setMetaData(metadata); + dispatch(setObjectMetadata(metadata)); setLoadingMetadata(false); }) .catch((err) => { @@ -228,69 +185,16 @@ const ObjectDetailPanel = ({ setLoadingMetadata(false); }); } - }, [bucketName, internalPaths, loadMetadata, actualInfo?.version_id]); - - let tagKeys: string[] = []; - - if (actualInfo && actualInfo.tags) { - tagKeys = Object.keys(actualInfo.tags); - } - - const openRetentionModal = () => { - setRetentionModalOpen(true); - }; - - const closeRetentionModal = (updateInfo: boolean) => { - setRetentionModalOpen(false); - if (updateInfo) { - dispatch(setLoadingObjectInfo(true)); - } - }; - - const shareObject = () => { - setShareFileModalOpen(true); - }; - - const closeShareModal = () => { - setObjectToShare(null); - setShareFileModalOpen(false); - }; + }, [ + bucketName, + internalPaths, + loadMetadata, + actualInfo?.version_id, + dispatch, + ]); const closeFileOpen = () => { - setLongFileOpen(false); - }; - - const closeDeleteModal = (closeAndReload: boolean) => { - setDeleteOpen(false); - - if (closeAndReload && selectedVersion === "") { - onClosePanel(true); - } else { - dispatch(setLoadingVersions(true)); - dispatch(setSelectedVersion("")); - dispatch(setLoadingObjectInfo(true)); - } - }; - - const closeAddTagModal = (reloadObjectData: boolean) => { - setTagModalOpen(false); - if (reloadObjectData) { - dispatch(setLoadingObjectInfo(true)); - } - }; - - const closeInspectModal = (reloadObjectData: boolean) => { - setInspectModalOpen(false); - if (reloadObjectData) { - dispatch(setLoadingObjectInfo(true)); - } - }; - - const closeLegalholdModal = (reload: boolean) => { - setLegalholdOpen(false); - if (reload) { - dispatch(setLoadingObjectInfo(true)); - } + dispatch(setLongFileOpen(false)); }; const loaderForContainer = ( @@ -312,211 +216,6 @@ const ObjectDetailPanel = ({ ? objectNameArray[objectNameArray.length - 1] : actualInfo.name; - const objectResources = [ - bucketName, - currentItem, - [bucketName, actualInfo.name].join("/"), - ]; - const canSetLegalHold = hasPermission(bucketName, [ - IAM_SCOPES.S3_PUT_OBJECT_LEGAL_HOLD, - IAM_SCOPES.S3_PUT_ACTIONS, - ]); - const canSetTags = hasPermission(objectResources, [ - IAM_SCOPES.S3_PUT_OBJECT_TAGGING, - IAM_SCOPES.S3_PUT_ACTIONS, - ]); - - const canChangeRetention = hasPermission( - objectResources, - [ - IAM_SCOPES.S3_GET_OBJECT_RETENTION, - IAM_SCOPES.S3_PUT_OBJECT_RETENTION, - IAM_SCOPES.S3_GET_ACTIONS, - IAM_SCOPES.S3_PUT_ACTIONS, - ], - true, - ); - const canInspect = hasPermission(objectResources, [ - IAM_SCOPES.ADMIN_INSPECT_DATA, - ]); - const canChangeVersioning = hasPermission(objectResources, [ - IAM_SCOPES.S3_GET_BUCKET_VERSIONING, - IAM_SCOPES.S3_PUT_BUCKET_VERSIONING, - IAM_SCOPES.S3_GET_OBJECT_VERSION, - IAM_SCOPES.S3_GET_ACTIONS, - IAM_SCOPES.S3_PUT_ACTIONS, - ]); - const canGetObject = hasPermission(objectResources, [ - IAM_SCOPES.S3_GET_OBJECT, - IAM_SCOPES.S3_GET_ACTIONS, - ]); - const canDelete = hasPermission( - [bucketName, currentItem, [bucketName, actualInfo.name].join("/")], - [IAM_SCOPES.S3_DELETE_OBJECT], - ); - - let objectType: AllowedPreviews = previewObjectType(metaData, currentItem); - - const multiActionButtons = [ - { - action: () => { - downloadObject(dispatch, bucketName, internalPaths, actualInfo); - }, - label: "Download", - disabled: !!actualInfo.is_delete_marker || !canGetObject, - icon: , - tooltip: canGetObject - ? "Download this Object" - : permissionTooltipHelper( - [IAM_SCOPES.S3_GET_OBJECT, IAM_SCOPES.S3_GET_ACTIONS], - "download this object", - ), - }, - { - action: () => { - shareObject(); - }, - label: "Share", - disabled: !!actualInfo.is_delete_marker || !canGetObject, - icon: , - tooltip: canGetObject - ? "Share this File" - : permissionTooltipHelper( - [IAM_SCOPES.S3_GET_OBJECT, IAM_SCOPES.S3_GET_ACTIONS], - "share this object", - ), - }, - { - action: () => { - setPreviewOpen(true); - }, - label: "Preview", - disabled: - !!actualInfo.is_delete_marker || - (objectType === "none" && !canGetObject), - icon: , - tooltip: canGetObject - ? "Preview this File" - : permissionTooltipHelper( - [IAM_SCOPES.S3_GET_OBJECT, IAM_SCOPES.S3_GET_ACTIONS], - "preview this object", - ), - }, - { - action: () => { - setLegalholdOpen(true); - }, - label: "Legal Hold", - disabled: - !locking || - !distributedSetup || - !!actualInfo.is_delete_marker || - !canSetLegalHold || - selectedVersion !== "", - icon: , - tooltip: canSetLegalHold - ? locking - ? "Change Legal Hold rules for this File" - : "Object Locking must be enabled on this bucket in order to set Legal Hold" - : permissionTooltipHelper( - [IAM_SCOPES.S3_PUT_OBJECT_LEGAL_HOLD, IAM_SCOPES.S3_PUT_ACTIONS], - "change legal hold settings for this object", - ), - }, - { - action: openRetentionModal, - label: "Retention", - disabled: - !distributedSetup || - !!actualInfo.is_delete_marker || - !canChangeRetention || - selectedVersion !== "" || - !locking, - icon: , - tooltip: canChangeRetention - ? locking - ? "Change Retention rules for this File" - : "Object Locking must be enabled on this bucket in order to set Retention Rules" - : permissionTooltipHelper( - [ - IAM_SCOPES.S3_GET_OBJECT_RETENTION, - IAM_SCOPES.S3_PUT_OBJECT_RETENTION, - IAM_SCOPES.S3_GET_ACTIONS, - IAM_SCOPES.S3_PUT_ACTIONS, - ], - "change Retention Rules for this object", - ), - }, - { - action: () => { - setTagModalOpen(true); - }, - label: "Tags", - disabled: - !!actualInfo.is_delete_marker || selectedVersion !== "" || !canSetTags, - icon: , - tooltip: canSetTags - ? "Change Tags for this File" - : permissionTooltipHelper( - [ - IAM_SCOPES.S3_PUT_OBJECT_TAGGING, - IAM_SCOPES.S3_GET_OBJECT_TAGGING, - IAM_SCOPES.S3_GET_ACTIONS, - IAM_SCOPES.S3_PUT_ACTIONS, - ], - "set Tags on this object", - ), - }, - { - action: () => { - setInspectModalOpen(true); - }, - label: "Inspect", - disabled: - !distributedSetup || - !!actualInfo.is_delete_marker || - selectedVersion !== "" || - !canInspect, - icon: , - tooltip: canInspect - ? "Inspect this file" - : permissionTooltipHelper( - [IAM_SCOPES.ADMIN_INSPECT_DATA], - "inspect this file", - ), - }, - { - action: () => { - dispatch( - setVersionsModeEnabled({ - status: !versionsMode, - objectName: objectName, - }), - ); - }, - label: versionsMode ? "Hide Object Versions" : "Display Object Versions", - icon: , - disabled: - !distributedSetup || - !(actualInfo.version_id && actualInfo.version_id !== "null") || - !canChangeVersioning, - tooltip: canChangeVersioning - ? actualInfo.version_id && actualInfo.version_id !== "null" - ? "Display Versions for this file" - : "" - : permissionTooltipHelper( - [ - IAM_SCOPES.S3_GET_BUCKET_VERSIONING, - IAM_SCOPES.S3_PUT_BUCKET_VERSIONING, - IAM_SCOPES.S3_GET_OBJECT_VERSION, - IAM_SCOPES.S3_GET_ACTIONS, - IAM_SCOPES.S3_PUT_ACTIONS, - ], - "display all versions of this object", - ), - }, - ]; - const calculateLastModifyTime = (lastModified: string) => { const currentTime = new Date(); const modifiedTime = new Date(lastModified); @@ -530,68 +229,6 @@ const ObjectDetailPanel = ({ return ( - {shareFileModalOpen && actualInfo && ( - - )} - {retentionModalOpen && actualInfo && ( - - )} - {deleteOpen && ( - - )} - {legalholdOpen && actualInfo && ( - - )} - {previewOpen && actualInfo && ( - { - setPreviewOpen(false); - }} - /> - )} - {tagModalOpen && actualInfo && ( - - )} - {inspectModalOpen && actualInfo && ( - - )} {longFileOpen && actualInfo && ( {loaderForContainer} ) : ( ({ + backgroundColor: theme.colors["Color/Neutral/Bg/colorBgShell"], + padding: `${theme.paddingSizes["sizeLG"]}px ${theme.paddingSizes["size"]}px`, + borderRadius: theme.borderRadius["borderRadiusLG"], + height: "100%", "& .ObjectDetailsTitle": { display: "flex", alignItems: "center", @@ -628,175 +269,30 @@ const ObjectDetailPanel = ({ "& .capitalizeFirst": { textTransform: "capitalize", }, - "& .detailContainer": { - padding: "0 22px", - marginBottom: 10, - fontSize: 14, - }, - }} + })} > - - {displayFileIconName(objectName || "", true)} - {objectName} - - } - items={multiActionButtons} - /> - ({ + display: "flex", + gap: 8, + alignItems: "center", + marginBottom: theme.paddingSizes["size"], + "& .objTitleName": { + overflowWrap: "break-word", + boxSizing: "border-box", + overflow: "hidden", + color: theme.colors["Color/Neutral/Text/colorTextHeading"], + }, + })} > - - - - - - } sx={{ marginBottom: 10 }} - /> + > + + File preview couldn't be displayed, Please try Download instead. + + + + + )} {!loading && !errorState && ( - - This is a File Preview for the first {arrayCreate.length} pages of - the document, if you wish to work with the full document please - download instead. - - - - - } sx={{ marginBottom: 10 }} - /> + > + + This is a File Preview for the first {arrayCreate.length} pages of + the document, if you wish to work with the full document please + download instead. + + + + + )} {!errorState && ( . -import React, { Fragment, useState } from "react"; -import { CSSObject } from "styled-components"; -import { Button, DropdownSelector, UploadFolderIcon, UploadIcon } from "mds"; +import React, { Fragment } from "react"; +import { ExpandMenu, ExpandMenuOption, FolderUpIcon, UploadIcon } from "mds"; import { IAM_SCOPES, permissionTooltipHelper, @@ -31,9 +30,8 @@ interface IUploadFilesButton { uploadPath: string; bucketName: string; forceDisable?: boolean; - uploadFileFunction: (closeFunction: () => void) => void; - uploadFolderFunction: (closeFunction: () => void) => void; - overrideStyles?: CSSObject; + uploadFileFunction: () => void; + uploadFolderFunction: () => void; } const UploadFilesButton = ({ @@ -42,10 +40,7 @@ const UploadFilesButton = ({ forceDisable = false, uploadFileFunction, uploadFolderFunction, - overrideStyles = {}, }: IUploadFilesButton) => { - const [anchorEl, setAnchorEl] = useState(null); - const [uploadOptionsOpen, uploadOptionsSetOpen] = useState(false); const anonymousMode = useSelector( (state: AppState) => state.system.anonymousMode, @@ -66,15 +61,6 @@ const UploadFilesButton = ({ putObjectPermScopes, ); - const openUploadMenu = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { - uploadOptionsSetOpen(!uploadOptionsOpen); - setAnchorEl(event.currentTarget); - }; - const handleCloseUpload = () => { - setAnchorEl(null); - }; - const uploadObjectAllowed = hasPermission( [uploadPath, ...sessionGrantWildCards], @@ -88,13 +74,13 @@ const UploadFilesButton = ({ true, ); - const uploadFolderAction = (action: string) => { + const uploadFilesAction = (action: string) => { if (action === "folder") { - uploadFolderFunction(handleCloseUpload); + uploadFolderFunction(); return; } - uploadFileFunction(handleCloseUpload); + uploadFileFunction(); }; const uploadEnabled: boolean = uploadObjectAllowed || uploadFolderAllowed; @@ -104,52 +90,43 @@ const UploadFilesButton = ({ - - handleSelectedOption(value)} - hideTriggerAction={() => { - handleCloseDownload(); - }} - open={openDownloadMenu} - anchorEl={anchorEl} - anchorOrigin={"end"} - /> - - - ); -}; - -export default DownloadWidgetDataButton; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/MergedWidgets.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/MergedWidgets.tsx deleted file mode 100644 index 75b280fac7..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/MergedWidgets.tsx +++ /dev/null @@ -1,42 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { Fragment } from "react"; -import CommonCard from "../CommonCard"; - -interface IMergedWidgets { - title: string; - leftComponent: any; - rightComponent: any; -} - -const MergedWidgets = ({ - title, - leftComponent, - rightComponent, -}: IMergedWidgets) => { - return ( - - - - ); -}; - -export default MergedWidgets; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx deleted file mode 100644 index a7a6d99031..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx +++ /dev/null @@ -1,348 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { Fragment, useState } from "react"; -import { - Box, - Button, - Grid, - HelpBox, - PageLayout, - ProgressBar, - PrometheusErrorIcon, - SyncIcon, - TabItemProps, - Tabs, -} from "mds"; -import { IDashboardPanel } from "./types"; -import { panelsConfiguration } from "./utils"; -import { componentToUse } from "./widgetUtils"; -import { useAppDispatch, useAppSelector } from "../../../../store"; -import { - DLayoutColumnProps, - DLayoutRowProps, - resourcesPanelsLayout, - resourcesPanelsLayoutAdvanced, - RowPanelLayout, - summaryPanelsLayout, - trafficPanelsLayout, -} from "./Widgets/LayoutUtil"; -import { getUsageAsync } from "../dashboardThunks"; -import { reloadWidgets } from "../dashboardSlice"; -import { selFeatures } from "../../consoleSlice"; -import { AdminInfoResponse } from "api/consoleApi"; -import ZoomWidget from "./ZoomWidget"; -import DateRangeSelector from "../../Common/FormComponents/DateRangeSelector/DateRangeSelector"; -import MergedWidgetsRenderer from "./Widgets/MergedWidgetsRenderer"; -import BasicDashboard from "../BasicDashboard/BasicDashboard"; - -interface IPrDashboard { - apiPrefix?: string; - usage: AdminInfoResponse | null; -} - -const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => { - const dispatch = useAppDispatch(); - const status = useAppSelector((state) => state.dashboard.status); - const zoomOpen = useAppSelector((state) => state.dashboard.zoom.openZoom); - const zoomWidget = useAppSelector( - (state) => state.dashboard.zoom.widgetRender, - ); - const features = useAppSelector(selFeatures); - const obOnly = !!features?.includes("object-browser-only"); - let hideMenu = false; - if (features?.includes("hide-menu")) { - hideMenu = true; - } else if (obOnly) { - hideMenu = true; - } - - const [timeStart, setTimeStart] = useState(null); - const [timeEnd, setTimeEnd] = useState(null); - const panelInformation = panelsConfiguration; - const [curTab, setCurTab] = useState("info"); - - const getPanelDetails = (id: number) => { - return panelInformation.find((panel) => panel.id === id); - }; - - const triggerLoad = () => { - dispatch(reloadWidgets()); - }; - - const renderCmpByConfig = ( - panelInfo: IDashboardPanel | undefined, - key: string, - ) => { - return ( - - {panelInfo ? ( - - - {panelInfo.mergedPanels ? ( - - ) : ( - componentToUse( - panelInfo, - timeStart, - timeEnd, - true, - apiPrefix, - zoomOpen, - ) - )} - - - ) : null} - - ); - }; - - const renderPanelItems = (layoutRows: DLayoutRowProps[]) => { - return layoutRows.reduce((prev: any[], rowItem, rIdx) => { - const { columns = [] } = rowItem; - const cellItems: any[] = columns.map( - (cellItem: DLayoutColumnProps, colIdx: number) => { - const panelInfo = getPanelDetails(cellItem.componentId); - return renderCmpByConfig(panelInfo, `${rIdx}-${colIdx}`); - }, - ); - const rowConfig = ( - - {cellItems} - - ); - return [...prev, rowConfig]; - }, []); - }; - - const renderSummaryPanels = () => { - return renderPanelItems(summaryPanelsLayout); - }; - - const renderTrafficPanels = () => { - return renderPanelItems(trafficPanelsLayout); - }; - - const renderResourcesPanels = () => { - return renderPanelItems(resourcesPanelsLayout); - }; - - const renderAdvancedResourcesPanels = () => { - return renderPanelItems(resourcesPanelsLayoutAdvanced); - }; - - const prometheusOptionsDisabled = - usage?.advancedMetricsStatus === "not configured"; - - const searchBox = ( - - {curTab === "info" ? ( - - - - Server Information - - - - - - - - ); -}; - -export default ExpandGraphLink; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/HealActivityRenderer.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/HealActivityRenderer.tsx deleted file mode 100644 index 2f32bcbdda..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/HealActivityRenderer.tsx +++ /dev/null @@ -1,70 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import { Box, breakPoints } from "mds"; -import TimeStatItem from "../../TimeStatItem"; - -export type SimpleWidgetRenderProps = { - valueToRender?: any; - loading?: boolean; - title?: any; - id?: number; - iconWidget?: any; -}; -const HealActivityRenderer = ({ - valueToRender = "", - loading = false, - iconWidget = null, -}: SimpleWidgetRenderProps) => { - return ( - - - - Time since last - {" "} - Heal Activity - - } - value={valueToRender} - /> - - ); -}; - -export default HealActivityRenderer; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/LayoutUtil.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/LayoutUtil.tsx deleted file mode 100644 index 24a678bcb4..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/LayoutUtil.tsx +++ /dev/null @@ -1,261 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import { Box } from "mds"; -import { CSSObject } from "styled-components"; -import { breakPoints } from "mds"; - -export type DLayoutColumnProps = { - componentId: number; - sx?: CSSObject; -}; -export type DLayoutRowProps = { - sx?: CSSObject; - columns: DLayoutColumnProps[]; -}; - -export const summaryPanelsLayout: DLayoutRowProps[] = [ - { - sx: { - minWidth: 0, - display: "grid", - gap: "30px", - gridTemplateColumns: "1fr 1fr 1fr 1fr", - [`@media (max-width: ${breakPoints.sm}px)`]: { - gridTemplateColumns: "1fr", - }, - [`@media (max-width: ${breakPoints.md}px)`]: { - gridTemplateColumns: "1fr 1fr", - }, - }, - columns: [ - { - componentId: 66, - }, - { - componentId: 44, - }, - { - componentId: 500, - }, - { - componentId: 501, - }, - ], - }, - { - sx: { - display: "grid", - minWidth: 0, // important to avoid css grid blow out. - gap: "30px", - gridTemplateColumns: "1fr 1fr", - [`@media (max-width: ${breakPoints.md}px)`]: { - gridTemplateColumns: "1fr", - }, - }, - columns: [ - { - componentId: 50, - }, - { - componentId: 502, - }, - ], - }, - { - sx: { - display: "grid", - minWidth: 0, - gap: "30px", - gridTemplateColumns: "1fr 1fr 1fr", - [`@media (max-width: ${breakPoints.md}px)`]: { - gridTemplateColumns: "1fr", - }, - }, - columns: [ - { - componentId: 80, - }, - { - componentId: 81, - }, - { - componentId: 1, - }, - ], - }, - { - sx: { - display: "grid", - minWidth: 0, - gap: "30px", - gridTemplateColumns: "1fr 1fr", - [`@media (max-width: ${breakPoints.md}px)`]: { - gridTemplateColumns: "1fr", - }, - }, - columns: [ - { - componentId: 68, - }, - { - componentId: 52, - }, - ], - }, - { - sx: { - display: "grid", - minWidth: 0, - gap: "30px", - gridTemplateColumns: "1fr 1fr", - [`@media (max-width: ${breakPoints.md}px)`]: { - gridTemplateColumns: "1fr", - }, - }, - columns: [ - { - componentId: 63, - }, - { - componentId: 70, - }, - ], - }, -]; - -export const trafficPanelsLayout: DLayoutRowProps[] = [ - { - sx: { - display: "grid", - gridTemplateColumns: "1fr", - gap: "30px", - }, - columns: [ - { - componentId: 60, - }, - ], - }, - { - sx: { - display: "grid", - minWidth: 0, - gap: "30px", - gridTemplateColumns: "1fr 1fr", - [`@media (max-width: ${breakPoints.md}px)`]: { - gridTemplateColumns: "1fr", - }, - }, - columns: [ - { - componentId: 71, - sx: { - flex: 1, - width: "50%", - flexShrink: 0, - }, - }, - { - componentId: 17, - sx: { - flex: 1, - width: "50%", - flexShrink: 0, - }, - }, - ], - }, - { - sx: { - display: "grid", - gridTemplateColumns: "1fr", - gap: "30px", - }, - columns: [ - { - componentId: 73, - }, - ], - }, -]; - -export const resourcesPanelsLayout: DLayoutRowProps[] = [ - { - sx: { - display: "grid", - minWidth: 0, - gridTemplateColumns: "1fr 1fr", - gap: "30px", - }, - columns: [ - { - componentId: 76, - }, - { - componentId: 77, - }, - ], - }, - { - sx: { - display: "grid", - minWidth: 0, - gridTemplateColumns: "1fr 1fr", - gap: "30px", - }, - columns: [ - { - componentId: 82, - }, - { - componentId: 74, - }, - ], - }, -]; -export const resourcesPanelsLayoutAdvanced: DLayoutRowProps[] = [ - { - sx: { - display: "grid", - minWidth: 0, - gridTemplateColumns: "1fr 1fr", - gap: "30px", - }, - columns: [ - { - componentId: 11, - }, - { - componentId: 8, - }, - ], - }, -]; - -export const RowPanelLayout = ({ children }: { children: any }) => { - return ( - - {children} - - ); -}; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx deleted file mode 100644 index 34debed952..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx +++ /dev/null @@ -1,414 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { Fragment, useEffect, useRef, useState } from "react"; -import styled from "styled-components"; -import get from "lodash/get"; -import { useSelector } from "react-redux"; -import { - Area, - AreaChart, - CartesianGrid, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis, -} from "recharts"; -import { Box, breakPoints, Grid, Loader } from "mds"; -import { ILinearGraphConfiguration } from "./types"; -import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary"; -import { IDashboardPanel } from "../types"; -import { widgetDetailsToPanel } from "../utils"; -import { ErrorResponseHandler } from "../../../../../common/types"; -import { setErrorSnackMessage } from "../../../../../systemSlice"; -import { AppState, useAppDispatch } from "../../../../../store"; -import api from "../../../../../common/api"; -import LineChartTooltip from "./tooltips/LineChartTooltip"; -import ExpandGraphLink from "./ExpandGraphLink"; -import DownloadWidgetDataButton from "../../DownloadWidgetDataButton"; - -interface ILinearGraphWidget { - title: string; - panelItem: IDashboardPanel; - timeStart: any; - timeEnd: any; - apiPrefix: string; - hideYAxis?: boolean; - yAxisFormatter?: (item: string) => string; - xAxisFormatter?: (item: string, var1: boolean, var2: boolean) => string; - areaWidget?: boolean; - zoomActivated?: boolean; -} - -const LinearGraphMain = styled.div(({ theme }) => ({ - ...widgetCommon(theme), - "& .chartCont": { - position: "relative", - height: 140, - width: "100%", - }, - "& .legendChart": { - display: "flex", - flexDirection: "column", - flex: "0 1 auto", - maxHeight: 130, - margin: 0, - overflowY: "auto", - position: "relative", - textAlign: "center", - width: "100%", - justifyContent: "flex-start", - color: get(theme, "mutedText", "#87888d"), - fontWeight: "bold", - fontSize: 12, - [`@media (max-width: ${breakPoints.md}px)`]: { - display: "none", - }, - }, - "& .loadingAlign": { - width: 40, - height: 40, - textAlign: "center", - margin: "15px auto", - }, -})); - -const LinearGraphWidget = ({ - title, - timeStart, - timeEnd, - panelItem, - apiPrefix, - hideYAxis = false, - areaWidget = false, - yAxisFormatter = (item: string) => item, - xAxisFormatter = (item: string, var1: boolean, var2: boolean) => item, - zoomActivated = false, -}: ILinearGraphWidget) => { - const dispatch = useAppDispatch(); - const [loading, setLoading] = useState(false); - const [hover, setHover] = useState(false); - const [data, setData] = useState([]); - const [csvData, setCsvData] = useState([]); - const [dataMax, setDataMax] = useState(0); - const [result, setResult] = useState(null); - const widgetVersion = useSelector( - (state: AppState) => state.dashboard.widgetLoadVersion, - ); - - const componentRef = useRef(null); - - useEffect(() => { - setLoading(true); - }, [widgetVersion]); - - useEffect(() => { - if (loading) { - let stepCalc = 0; - if (timeStart !== null && timeEnd !== null) { - const secondsInPeriod = - timeEnd.toUnixInteger() - timeStart.toUnixInteger(); - const periods = Math.floor(secondsInPeriod / 60); - - stepCalc = periods < 1 ? 15 : periods; - } - - api - .invoke( - "GET", - `/api/v1/${apiPrefix}/info/widgets/${ - panelItem.id - }/?step=${stepCalc}&${ - timeStart !== null ? `&start=${timeStart.toUnixInteger()}` : "" - }${timeStart !== null && timeEnd !== null ? "&" : ""}${ - timeEnd !== null ? `end=${timeEnd.toUnixInteger()}` : "" - }`, - ) - .then((res: any) => { - const widgetsWithValue = widgetDetailsToPanel(res, panelItem); - setData(widgetsWithValue.data); - setResult(widgetsWithValue); - setLoading(false); - let maxVal = 0; - for (const dp of widgetsWithValue.data) { - for (const key in dp) { - if (key === "name") { - continue; - } - let val = parseInt(dp[key]); - - if (isNaN(val)) { - val = 0; - } - - if (maxVal < val) { - maxVal = val; - } - } - } - setDataMax(maxVal); - }) - .catch((err: ErrorResponseHandler) => { - dispatch(setErrorSnackMessage(err)); - setLoading(false); - }); - } - }, [loading, panelItem, timeEnd, timeStart, dispatch, apiPrefix]); - - let intervalCount = Math.floor(data.length / 5); - - const onHover = () => { - setHover(true); - }; - - const onStopHover = () => { - setHover(false); - }; - - useEffect(() => { - const fmtData = data.map((el: any) => { - const date = new Date(el?.name * 1000); - return { - ...el, - name: date, - }; - }); - - setCsvData(fmtData); - }, [data]); - - const linearConfiguration = result - ? (result?.widgetConfiguration as ILinearGraphConfiguration[]) - : []; - - const CustomizedDot = (prop: any) => { - const { cx, cy, index } = prop; - - if (index % 3 !== 0) { - return null; - } - return ; - }; - - let dspLongDate = false; - - if (zoomActivated) { - dspLongDate = true; - } - - return ( - - - {!zoomActivated && ( - - - {title} - - - {hover && } - - - {componentRef !== null && ( - - )} - - - )} -
- - {loading && } - {!loading && ( - - - - - {areaWidget && ( - - - - - - - - - )} - - - xAxisFormatter(value, dspLongDate, true) - } - interval={intervalCount} - tick={{ - fontSize: "68%", - fontWeight: "normal", - color: "#404143", - }} - tickCount={10} - stroke={"#082045"} - /> - yAxisFormatter(value)} - tick={{ - fontSize: "68%", - fontWeight: "normal", - color: "#404143", - }} - stroke={"#082045"} - /> - {linearConfiguration.map((section, index) => { - return ( - : false} - /> - ); - })} - - } - wrapperStyle={{ - zIndex: 5000, - }} - /> - - - - {!areaWidget && ( - - {zoomActivated && ( - - Series -
-
-
- )} - - - {linearConfiguration.map((section, index) => { - return ( - - - - {section.keyLabel} - - - ); - })} - -
- )} -
- )} -
-
-
-
- ); -}; - -export default LinearGraphWidget; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/MergedWidgetsRenderer.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/MergedWidgetsRenderer.tsx deleted file mode 100644 index 32d464c1c6..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/MergedWidgetsRenderer.tsx +++ /dev/null @@ -1,92 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import { componentToUse } from "../widgetUtils"; -import { IDashboardPanel } from "../types"; -import MergedWidgets from "../MergedWidgets"; -import EntityStateItemRenderer from "./EntityStateItemRenderer"; -import NetworkItem from "./NetworkItem"; -import DashboardItemBox from "../../DashboardItemBox"; - -const MergedWidgetsRenderer = ({ - info, - timeStart, - timeEnd, - loading, - apiPrefix, -}: { - info: IDashboardPanel; - timeStart: any; - timeEnd: any; - loading: boolean; - apiPrefix: string; -}) => { - const { mergedPanels = [], title = "", id } = info; - const [leftPanel, rightPanel] = mergedPanels; - - const renderById = () => { - if ([500, 501].includes(id)) { - return ( - - - - ); - } - - if (id === 502) { - return ( - - - - ); - } - - return ( - - ); - }; - - return renderById(); -}; - -export default MergedWidgetsRenderer; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NetworkGetItem.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NetworkGetItem.tsx deleted file mode 100644 index 83e961574b..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NetworkGetItem.tsx +++ /dev/null @@ -1,72 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import styled from "styled-components"; -import get from "lodash/get"; -import { Loader, NetworkGetIcon, Box } from "mds"; - -const NetworkGetBase = styled.div(({ theme }) => ({ - "& .putLabel": { - display: "flex", - gap: 10, - alignItems: "center", - marginTop: "10px", - - "& .min-icon": { - height: 15, - width: 15, - fill: get(theme, "signalColors.good", "#4CCB92"), - }, - - "& .getText": { - fontSize: "18px", - color: get(theme, "mutedText", "#87888d"), - fontWeight: "bold", - }, - "& .valueText": { - fontSize: 50, - fontFamily: "Inter", - fontWeight: 600, - }, - }, -})); - -const NetworkGetItem = ({ - value, - loading, -}: { - value: any; - loading: boolean; - title?: any; - id?: number; -}) => { - return ( - - - GET - {loading ? ( - - ) : ( - - )} - - {value} - - ); -}; - -export default NetworkGetItem; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NetworkItem.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NetworkItem.tsx deleted file mode 100644 index 2c4dfc795d..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NetworkItem.tsx +++ /dev/null @@ -1,176 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import styled from "styled-components"; -import get from "lodash/get"; -import { Box, breakPoints, SpeedtestIcon } from "mds"; -import { IDashboardPanel } from "../types"; -import SingleValueWidget from "./SingleValueWidget"; -import NetworkGetItem from "./NetworkGetItem"; -import NetworkPutItem from "./NetworkPutItem"; - -const NetworkItemBase = styled.div(({ theme }) => ({ - flex: 1, - display: "flex", - alignItems: "center", - flexFlow: "row", - gap: "15px", - "& .unitText": { - fontSize: "14px", - color: get(theme, "mutedText", "#87888d"), - marginLeft: "5px", - }, - "& .unit": { - color: get(theme, "mutedText", "#87888d"), - fontSize: "18px", - marginLeft: "12px", - marginTop: "10px", - }, - [`@media (max-width: ${breakPoints.sm}px)`]: { - flexFlow: "column", - }, -})); - -const NetworkItem = ({ - value, - timeStart, - timeEnd, - apiPrefix, -}: { - value: IDashboardPanel; - timeStart: any; - timeEnd: any; - apiPrefix: string; -}) => { - const { mergedPanels = [] } = value; - const [leftPanel, rightPanel] = mergedPanels; - - const rightCmp = ( - { - return ( - - ); - }} - /> - ); - const leftCmp = ( - { - return ( - - ); - }} - /> - ); - - return ( - - - Network - - - - {leftCmp} - - - - - {rightCmp} - - - - - - - ); -}; - -export default NetworkItem; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NetworkPutItem.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NetworkPutItem.tsx deleted file mode 100644 index 3cfd5e9834..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NetworkPutItem.tsx +++ /dev/null @@ -1,72 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2022 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import styled from "styled-components"; -import get from "lodash/get"; -import { Box, Loader, NetworkPutIcon } from "mds"; - -const NetworkPutBase = styled.div(({ theme }) => ({ - "& .putLabel": { - display: "flex", - gap: 10, - alignItems: "center", - marginTop: "10px", - - "& .min-icon": { - height: 15, - width: 15, - fill: get(theme, "signalColors.info", "#2781B0"), - }, - - "& .putText": { - fontSize: "18px", - color: get(theme, "mutedText", "#87888d"), - fontWeight: "bold", - }, - "& .valueText": { - fontSize: 50, - fontFamily: "Inter", - fontWeight: 600, - }, - }, -})); - -const NetworkPutItem = ({ - value, - loading, -}: { - value: any; - loading: boolean; - title?: any; - id?: number; -}) => { - return ( - - - PUT - {loading ? ( - - ) : ( - - )} - - {value} - - ); -}; - -export default NetworkPutItem; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NumericStatCard.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NumericStatCard.tsx deleted file mode 100644 index eed4dcf20d..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/NumericStatCard.tsx +++ /dev/null @@ -1,134 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2022 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import styled from "styled-components"; -import get from "lodash/get"; -import { Box, breakPoints, Loader, Tooltip } from "mds"; - -const StatCardMain = styled.div(({ theme }) => ({ - fontFamily: "Inter,sans-serif", - color: get(theme, "signalColors.main", "#07193E"), - maxWidth: "300px", - display: "flex", - marginLeft: "auto", - marginRight: "auto", - cursor: "default", - position: "relative", - width: "100%", -})); - -const NumericStatCard = ({ - value, - label = "", - icon = null, - loading = false, -}: { - value: string | number; - label?: any; - icon?: any; - loading?: boolean; -}) => { - const getContent = () => { - return ( - - - - {label} - - - - - {value} - - - - - {} - {loading ? ( - - ) : ( - icon - )} - - - ); - }; - - return {getContent()}; -}; - -export default NumericStatCard; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/PieChartWidget.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/PieChartWidget.tsx deleted file mode 100644 index 01e90e3dc7..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/PieChartWidget.tsx +++ /dev/null @@ -1,237 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { useEffect, useState } from "react"; -import get from "lodash/get"; -import styled from "styled-components"; -import { Box, Loader } from "mds"; -import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts"; -import { useSelector } from "react-redux"; -import api from "../../../../../common/api"; -import { IPieChartConfiguration } from "./types"; -import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary"; -import { IDashboardPanel } from "../types"; -import { splitSizeMetric, widgetDetailsToPanel } from "../utils"; -import { ErrorResponseHandler } from "../../../../../common/types"; -import { setErrorSnackMessage } from "../../../../../systemSlice"; -import { AppState, useAppDispatch } from "../../../../../store"; - -interface IPieChartWidget { - title: string; - panelItem: IDashboardPanel; - timeStart: any; - timeEnd: any; - apiPrefix: string; -} - -const PieChartMain = styled.div(({ theme }) => ({ - ...widgetCommon(theme), - "& .loadingAlign": { - width: "100%", - paddingTop: "15px", - textAlign: "center", - margin: "auto", - }, - "& .pieChartLabel": { - fontSize: 60, - color: get(theme, "signalColors.main", "#07193E"), - fontWeight: "bold", - width: "100%", - "& .unitText": { - color: get(theme, "mutedText", "#87888d"), - fontSize: 12, - }, - }, - "& .chartContainer": { - width: "100%", - height: 140, - }, -})); - -const PieChartWidget = ({ - title, - panelItem, - timeStart, - timeEnd, - apiPrefix, -}: IPieChartWidget) => { - const dispatch = useAppDispatch(); - const [loading, setLoading] = useState(false); - const [dataInner, setDataInner] = useState([]); - const [dataOuter, setDataOuter] = useState([]); - const [result, setResult] = useState(null); - const widgetVersion = useSelector( - (state: AppState) => state.dashboard.widgetLoadVersion, - ); - - useEffect(() => { - setLoading(true); - }, [widgetVersion]); - - useEffect(() => { - if (loading) { - let stepCalc = 0; - if (timeStart !== null && timeEnd !== null) { - const secondsInPeriod = - timeEnd.toUnixInteger() - timeStart.toUnixInteger(); - const periods = Math.floor(secondsInPeriod / 60); - - stepCalc = periods < 1 ? 15 : periods; - } - - api - .invoke( - "GET", - `/api/v1/${apiPrefix}/info/widgets/${ - panelItem.id - }/?step=${stepCalc}&${ - timeStart !== null ? `&start=${timeStart.toUnixInteger()}` : "" - }${timeStart !== null && timeEnd !== null ? "&" : ""}${ - timeEnd !== null ? `end=${timeEnd.toUnixInteger()}` : "" - }`, - ) - .then((res: any) => { - const widgetsWithValue = widgetDetailsToPanel(res, panelItem); - setDataInner(widgetsWithValue.data); - setDataOuter(widgetsWithValue.dataOuter as object[]); - setResult(widgetsWithValue); - setLoading(false); - }) - .catch((err: ErrorResponseHandler) => { - dispatch(setErrorSnackMessage(err)); - setLoading(false); - }); - } - }, [loading, panelItem, timeEnd, timeStart, dispatch, apiPrefix]); - - const pieChartConfiguration = result - ? (result.widgetConfiguration as IPieChartConfiguration) - : []; - const middleLabel = result?.innerLabel; - - const innerColors = get(pieChartConfiguration, "innerChart.colorList", []); - const outerColors = get(pieChartConfiguration, "outerChart.colorList", []); - - return ( - - - {title} - {loading && ( - - - - )} - {!loading && ( - - - {middleLabel && splitSizeMetric(middleLabel)} - - - - - {dataOuter && ( - - {dataOuter.map((entry, index) => ( - - ))} - - )} - {dataInner && ( - - {dataInner.map((entry, index) => { - return ( - - ); - })} - - )} - - - - - )} - - - ); -}; - -export default PieChartWidget; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/ScanActivityRenderer.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/ScanActivityRenderer.tsx deleted file mode 100644 index ae7d94ecfa..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/ScanActivityRenderer.tsx +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import { Box, breakPoints } from "mds"; -import TimeStatItem from "../../TimeStatItem"; -import { SimpleWidgetRenderProps } from "./HealActivityRenderer"; - -const ScanActivityRenderer = ({ - valueToRender = "", - loading = false, - iconWidget = null, -}: SimpleWidgetRenderProps) => { - return ( - - - - Time since last - {" "} - Scan Activity - - } - value={valueToRender} - /> - - ); -}; - -export default ScanActivityRenderer; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/SimpleWidget.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/SimpleWidget.tsx deleted file mode 100644 index 6d76f31b60..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/SimpleWidget.tsx +++ /dev/null @@ -1,140 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { Fragment, useEffect, useState } from "react"; -import styled from "styled-components"; -import get from "lodash/get"; -import { useSelector } from "react-redux"; -import { Loader } from "mds"; -import api from "../../../../../common/api"; -import { widgetDetailsToPanel } from "../utils"; -import { IDashboardPanel } from "../types"; -import { ErrorResponseHandler } from "../../../../../common/types"; -import { setErrorSnackMessage } from "../../../../../systemSlice"; -import { AppState, useAppDispatch } from "../../../../../store"; - -interface ISimpleWidget { - iconWidget: any; - title: string; - panelItem: IDashboardPanel; - timeStart: any; - timeEnd: any; - apiPrefix: string; - renderFn?: undefined | null | ((arg: Record) => any); -} - -const SimpleWidgetMain = styled.span(({ theme }) => ({ - display: "inline-flex", - color: get(theme, "signalColors.main", "#07193E"), - alignItems: "center", - "& .icon": { - color: get(theme, "signalColors.main", "#07193E"), - fill: get(theme, "signalColors.main", "#07193E"), - marginRight: 5, - marginLeft: 12, - }, - "& .widgetLabel": { - fontWeight: "bold", - textTransform: "uppercase", - marginRight: 10, - }, - "& .widgetValue": { - marginRight: 25, - }, -})); - -const SimpleWidget = ({ - iconWidget, - title, - panelItem, - timeStart, - timeEnd, - apiPrefix, - renderFn, -}: ISimpleWidget) => { - const dispatch = useAppDispatch(); - const [loading, setLoading] = useState(false); - const [data, setData] = useState(""); - const widgetVersion = useSelector( - (state: AppState) => state.dashboard.widgetLoadVersion, - ); - - useEffect(() => { - setLoading(true); - }, [widgetVersion]); - - useEffect(() => { - if (loading) { - let stepCalc = 0; - if (timeStart !== null && timeEnd !== null) { - const secondsInPeriod = - timeEnd.toUnixInteger() - timeStart.toUnixInteger(); - const periods = Math.floor(secondsInPeriod / 60); - - stepCalc = periods < 1 ? 15 : periods; - } - - api - .invoke( - "GET", - `/api/v1/${apiPrefix}/info/widgets/${ - panelItem.id - }/?step=${stepCalc}&${ - timeStart !== null ? `&start=${timeStart.toUnixInteger()}` : "" - }${timeStart !== null && timeEnd !== null ? "&" : ""}${ - timeEnd !== null ? `end=${timeEnd.toUnixInteger()}` : "" - }`, - ) - .then((res: any) => { - const widgetsWithValue = widgetDetailsToPanel(res, panelItem); - setData(widgetsWithValue.data); - setLoading(false); - }) - .catch((err: ErrorResponseHandler) => { - dispatch(setErrorSnackMessage(err)); - setLoading(false); - }); - } - }, [loading, panelItem, timeEnd, timeStart, dispatch, apiPrefix]); - - if (renderFn) { - return renderFn({ - valueToRender: data, - loading, - title, - id: panelItem.id, - iconWidget: iconWidget, - }); - } - return ( - - {loading && ( -
- -
- )} - {!loading && ( - - {iconWidget ? iconWidget : null} - {title}: - {data} - - )} -
- ); -}; - -export default SimpleWidget; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/SingleRepWidget.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/SingleRepWidget.tsx deleted file mode 100644 index 6f2c2b77f9..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/SingleRepWidget.tsx +++ /dev/null @@ -1,142 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { useEffect, useState } from "react"; -import { connect, useSelector } from "react-redux"; -import { IDashboardPanel } from "../types"; -import { widgetDetailsToPanel } from "../utils"; -import { ErrorResponseHandler } from "../../../../../common/types"; -import { representationNumber } from "../../../../../common/utils"; -import api from "../../../../../common/api"; -import DashboardItemBox from "../../DashboardItemBox"; -import BucketsCountItem from "./BucketsCountItem"; -import ObjectsCountItem from "./ObjectsCountItem"; -import { setErrorSnackMessage } from "../../../../../systemSlice"; -import { AppState, useAppDispatch } from "../../../../../store"; - -interface ISingleRepWidget { - title: string; - panelItem: IDashboardPanel; - timeStart: any; - timeEnd: any; - propLoading: boolean; - - color?: string; - fillColor?: string; - apiPrefix: string; -} - -const SingleRepWidget = ({ - title, - panelItem, - timeStart, - timeEnd, - propLoading, - - apiPrefix, -}: ISingleRepWidget) => { - const dispatch = useAppDispatch(); - const [loading, setLoading] = useState(false); - const [result, setResult] = useState(null); - const widgetVersion = useSelector( - (state: AppState) => state.dashboard.widgetLoadVersion, - ); - - useEffect(() => { - setLoading(true); - }, [widgetVersion]); - - useEffect(() => { - if (loading) { - let stepCalc = 0; - if (timeStart !== null && timeEnd !== null) { - const secondsInPeriod = - timeEnd.toUnixInteger() - timeStart.toUnixInteger(); - const periods = Math.floor(secondsInPeriod / 60); - - stepCalc = periods < 1 ? 15 : periods; - } - - api - .invoke( - "GET", - `/api/v1/${apiPrefix}/info/widgets/${ - panelItem.id - }/?step=${stepCalc}&${ - timeStart !== null ? `&start=${timeStart.toUnixInteger()}` : "" - }${timeStart !== null && timeEnd !== null ? "&" : ""}${ - timeEnd !== null ? `end=${timeEnd.toUnixInteger()}` : "" - }`, - ) - .then((res: any) => { - const widgetsWithValue = widgetDetailsToPanel(res, panelItem); - setResult(widgetsWithValue); - setLoading(false); - }) - .catch((err: ErrorResponseHandler) => { - dispatch(setErrorSnackMessage(err)); - setLoading(false); - }); - } - }, [loading, panelItem, timeEnd, timeStart, dispatch, apiPrefix]); - - let repNumber = ""; - - if (result) { - const resultRep = parseInt(result.innerLabel || "0"); - - if (!isNaN(resultRep)) { - repNumber = representationNumber(resultRep); - } else { - repNumber = "0"; - } - } - - const renderById = (id: number) => { - if (id === 66) { - return ( - - - - ); - } - if (id === 44) { - return ( - - - - ); - } - - return null; - }; - - return renderById(panelItem.id); -}; - -const connector = connect(null, { - setErrorSnackMessage: setErrorSnackMessage, -}); - -export default connector(SingleRepWidget); diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/SingleValueWidget.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/SingleValueWidget.tsx deleted file mode 100644 index 630c829fda..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/SingleValueWidget.tsx +++ /dev/null @@ -1,143 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { Fragment, useEffect, useState } from "react"; -import { useSelector } from "react-redux"; -import { Box, Loader } from "mds"; -import styled from "styled-components"; -import get from "lodash/get"; -import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary"; -import { splitSizeMetric, widgetDetailsToPanel } from "../utils"; -import { IDashboardPanel } from "../types"; -import { ErrorResponseHandler } from "../../../../../common/types"; -import { setErrorSnackMessage } from "../../../../../systemSlice"; -import { AppState, useAppDispatch } from "../../../../../store"; -import api from "../../../../../common/api"; - -interface ISingleValueWidget { - title: string; - panelItem: IDashboardPanel; - timeStart: any; - timeEnd: any; - apiPrefix: string; - renderFn?: (arg: Record) => any; -} - -const SingleValueWidgetMain = styled.div(({ theme }) => ({ - display: "flex", - height: 140, - flexDirection: "column", - justifyContent: "center", - "& .unitText": { - color: get(theme, "mutedText", "#87888d"), - fontSize: 12, - }, - "& .loadingAlign": { - width: "100%", - textAlign: "center", - margin: "auto", - }, - "& .metric": { - fontSize: 60, - lineHeight: 1, - color: get(theme, "signalColors.main", "#07193E"), - fontWeight: 700, - }, - "& .titleElement": { - fontSize: 10, - color: get(theme, "mutedText", "#87888d"), - fontWeight: 700, - }, - ...widgetCommon(theme), -})); - -const SingleValueWidget = ({ - title, - panelItem, - timeStart, - timeEnd, - apiPrefix, - renderFn, -}: ISingleValueWidget) => { - const dispatch = useAppDispatch(); - - const [loading, setLoading] = useState(false); - const [data, setData] = useState(""); - const widgetVersion = useSelector( - (state: AppState) => state.dashboard.widgetLoadVersion, - ); - - useEffect(() => { - setLoading(true); - }, [widgetVersion]); - - useEffect(() => { - if (loading) { - let stepCalc = 0; - if (timeStart !== null && timeEnd !== null) { - const secondsInPeriod = - timeEnd.toUnixInteger() - timeStart.toUnixInteger(); - const periods = Math.floor(secondsInPeriod / 60); - - stepCalc = periods < 1 ? 15 : periods; - } - - api - .invoke( - "GET", - `/api/v1/${apiPrefix}/info/widgets/${ - panelItem.id - }/?step=${stepCalc}&${ - timeStart !== null ? `&start=${timeStart.toUnixInteger()}` : "" - }${timeStart !== null && timeEnd !== null ? "&" : ""}${ - timeEnd !== null ? `end=${timeEnd.toUnixInteger()}` : "" - }`, - ) - .then((res: any) => { - const widgetsWithValue = widgetDetailsToPanel(res, panelItem); - setData(widgetsWithValue.data); - setLoading(false); - }) - .catch((err: ErrorResponseHandler) => { - dispatch(setErrorSnackMessage(err)); - setLoading(false); - }); - } - }, [loading, panelItem, timeEnd, timeStart, dispatch, apiPrefix]); - - const valueToRender = splitSizeMetric(data); - - if (renderFn) { - return renderFn({ valueToRender, loading, title, id: panelItem.id }); - } - return ( - - {loading && ( - - - - )} - {!loading && ( - - {splitSizeMetric(data)} - {title} - - )} - - ); -}; - -export default SingleValueWidget; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/tooltips/BarChartTooltip.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/tooltips/BarChartTooltip.tsx deleted file mode 100644 index 2533391d1a..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/tooltips/BarChartTooltip.tsx +++ /dev/null @@ -1,64 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import { Box } from "mds"; -import { tooltipCommon } from "../../../../Common/FormComponents/common/styleLibrary"; - -const BarChartTooltip = ({ - active, - payload, - label, - barChartConfiguration, -}: any) => { - if (active) { - return ( - - {label} - {payload && - payload.map((pl: any, index: number) => { - return ( - - - - {pl.value} - - - ); - })} - - ); - } - - return null; -}; - -export default BarChartTooltip; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/tooltips/LineChartTooltip.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/tooltips/LineChartTooltip.tsx deleted file mode 100644 index c0e98123dc..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/tooltips/LineChartTooltip.tsx +++ /dev/null @@ -1,71 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import { Box } from "mds"; -import { getTimeFromTimestamp } from "../../../../../../common/utils"; -import { tooltipCommon } from "../../../../Common/FormComponents/common/styleLibrary"; - -const LineChartTooltip = ({ - active, - payload, - label, - linearConfiguration, - yAxisFormatter, -}: any) => { - if (active) { - return ( - - - {getTimeFromTimestamp(label, true)} - - {payload && - payload.map((pl: any, index: number) => { - return ( - - - - - {linearConfiguration[index].keyLabel}:{" "} - {yAxisFormatter(pl.value)} - - - - ); - })} - - ); - } - - return null; -}; - -export default LineChartTooltip; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/types.ts b/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/types.ts deleted file mode 100644 index fc00801fef..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/types.ts +++ /dev/null @@ -1,54 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -export interface ILinearGraphConfiguration { - dataKey: string; - keyLabel: string; - lineColor: string; - fillColor: string; - strokeWidth?: number; -} - -export interface IBarChartConfiguration { - dataKey: string; - color: string; - background?: object; - greatestColor?: string; - strokeWidth?: number; -} - -export interface IPieChartConfiguration { - innerChart: ISinglePieConfiguration; - outerChart?: ISinglePieConfiguration; - strokeWidth?: number; -} - -interface ISinglePieConfiguration { - colorList: string[]; - startAngle?: number; - endAngle?: number; - innerRadius?: number | string; - outerRadius?: number | string; -} - -export interface IDataSRep { - value: number; -} - -export interface IBarChartRelation { - originTag: string; - displayTag: string; -} diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/ZoomWidget.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/ZoomWidget.tsx deleted file mode 100644 index 0fde64aea1..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/ZoomWidget.tsx +++ /dev/null @@ -1,65 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { Fragment } from "react"; - -import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper"; -import { IDashboardPanel } from "./types"; -import { componentToUse } from "./widgetUtils"; -import { closeZoomPage } from "../dashboardSlice"; -import { useAppDispatch } from "../../../../store"; - -interface IZoomWidget { - widgetRender: number; - value: IDashboardPanel | null; - modalOpen: boolean; - timeStart: any; - timeEnd: any; - apiPrefix: string; -} - -const ZoomWidget = ({ - value, - modalOpen, - timeStart, - timeEnd, - apiPrefix, -}: IZoomWidget) => { - const dispatch = useAppDispatch(); - if (!value) { - return null; - } - - return ( - { - dispatch(closeZoomPage()); - }} - modalOpen={modalOpen} - wideLimit={false} - sx={{ - padding: 0, - }} - > - - {componentToUse(value, timeStart, timeEnd, true, apiPrefix, true)} - - - ); -}; - -export default ZoomWidget; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/types.ts b/web-app/src/screens/Console/Dashboard/Prometheus/types.ts deleted file mode 100644 index 9f62a5fcdd..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/types.ts +++ /dev/null @@ -1,55 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import { - IBarChartConfiguration, - IBarChartRelation, - IDataSRep, - ILinearGraphConfiguration, - IPieChartConfiguration, -} from "./Widgets/types"; - -export enum widgetType { - singleValue = "singleValue", - linearGraph = "linearGraph", - areaGraph = "areaGraph", - barChart = "barChart", - pieChart = "pieChart", - singleRep = "singleRep", - simpleWidget = "simpleWidget", -} - -export interface IDashboardPanel { - id: number; - mergedPanels?: IDashboardPanel[]; - title: string; - data?: string | object[] | IDataSRep[]; - dataOuter?: string | object[]; - type?: widgetType; - widgetIcon?: any; - widgetConfiguration?: - | ILinearGraphConfiguration[] - | IBarChartConfiguration[] - | IPieChartConfiguration; - color?: string; - fillColor?: string; - innerLabel?: string; - labelDisplayFunction?: (value: string) => any; - disableYAxis?: boolean; - xAxisFormatter?: (item: string) => string; - yAxisFormatter?: (item: string) => string; - customStructure?: IBarChartRelation[]; -} diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/utils.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/utils.tsx deleted file mode 100644 index bc97e45c71..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/utils.tsx +++ /dev/null @@ -1,849 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { Fragment } from "react"; -import get from "lodash/get"; -import { IDashboardPanel, widgetType } from "./types"; -import { - getTimeFromTimestamp, - niceBytes, - niceDays, - representationNumber, - textToRGBColor, - units, -} from "../../../../common/utils"; -import { DiagnosticsIcon, HealIcon, UptimeIcon } from "mds"; - -const colorsMain = [ - "#C4D4E9", - "#DCD1EE", - "#D1EEE7", - "#EEDED1", - "#AAF38F", - "#F9E6C5", - "#C83B51", - "#F4CECE", - "#D6D6D6", -]; - -const niceDaysFromNS = (seconds: string) => { - return niceDays(seconds, "ns"); -}; - -const roundNumber = (value: string) => { - return parseInt(value).toString(10); -}; - -export const panelsConfiguration: IDashboardPanel[] = [ - { - id: 1, - title: "Uptime", - data: "N/A", - type: widgetType.simpleWidget, - widgetIcon: , - labelDisplayFunction: niceDays, - }, - { - id: 50, - title: "Capacity", - data: [], - dataOuter: [{ name: "outer", value: 100 }], - widgetConfiguration: { - outerChart: { - colorList: ["#9c9c9c"], - innerRadius: 0, - outerRadius: 0, - startAngle: 0, - endAngle: 0, - }, - innerChart: { - colorList: colorsMain, - innerRadius: 20, - outerRadius: 50, - startAngle: 90, - endAngle: -200, - }, - }, - type: widgetType.pieChart, - innerLabel: "N/A", - labelDisplayFunction: niceBytes, - }, - { - id: 51, - title: "Usable Capacity", - data: [], - dataOuter: [{ name: "outer", value: 100 }], - widgetConfiguration: { - outerChart: { - colorList: ["#9c9c9c"], - innerRadius: 0, - outerRadius: 0, - startAngle: 0, - endAngle: 0, - }, - innerChart: { - colorList: colorsMain, - innerRadius: 20, - outerRadius: 50, - startAngle: 90, - endAngle: -200, - }, - }, - type: widgetType.pieChart, - innerLabel: "N/A", - labelDisplayFunction: niceBytes, - }, - { - id: 68, - title: "Data Usage Growth", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.areaGraph, - yAxisFormatter: niceBytes, - xAxisFormatter: getTimeFromTimestamp, - }, - { - id: 52, - title: "Object size distribution", - data: [], - widgetConfiguration: [ - { - dataKey: "a", - color: "#2781B0", - background: { - fill: "#EEF1F4", - }, - greatestColor: "#081C42", - }, - ], - customStructure: [ - { originTag: "LESS_THAN_1024_B", displayTag: "Less than 1024B" }, - { - originTag: "BETWEEN_1024_B_AND_1_MB", - displayTag: "Between 1024B and 1MB", - }, - { - originTag: "BETWEEN_1_MB_AND_10_MB", - displayTag: "Between 1MB and 10MB", - }, - { - originTag: "BETWEEN_10_MB_AND_64_MB", - displayTag: "Between 10MB and 64MB", - }, - { - originTag: "BETWEEN_64_MB_AND_128_MB", - displayTag: "Between 64MB and 128MB", - }, - { - originTag: "BETWEEN_128_MB_AND_512_MB", - displayTag: "Between 128MB and 512MB", - }, - { - originTag: "GREATER_THAN_512_MB", - displayTag: "Greater than 512MB", - }, - ], - type: widgetType.barChart, - }, - { - id: 66, - title: "Buckets", - data: [], - innerLabel: "N/A", - type: widgetType.singleRep, - color: "#0071BC", - fillColor: "#ADD5E0", - }, - { - id: 44, - title: "Objects", - data: [], - innerLabel: "N/A", - type: widgetType.singleRep, - color: "#0071BC", - fillColor: "#ADD5E0", - }, - { - id: 63, - title: "API Data Received Rate", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - strokeWidth: 3, - }, - ], - type: widgetType.linearGraph, - - xAxisFormatter: getTimeFromTimestamp, - yAxisFormatter: niceBytes, - }, - { - id: 61, - title: "Total Open FDs", - data: [], - innerLabel: "N/A", - type: widgetType.singleRep, - color: "#22B573", - fillColor: "#A6E8C4", - }, - { - id: 62, - title: "Total Goroutines", - data: [], - innerLabel: "N/A", - type: widgetType.singleRep, - color: "#F7655E", - fillColor: "#F4CECE", - }, - { - id: 77, - title: "Node CPU Usage", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - - yAxisFormatter: roundNumber, - xAxisFormatter: getTimeFromTimestamp, - }, - { - id: 60, - title: "API Request Rate", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - yAxisFormatter: roundNumber, - xAxisFormatter: getTimeFromTimestamp, - }, - { - id: 70, - title: "API Data Sent Rate", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - - xAxisFormatter: getTimeFromTimestamp, - yAxisFormatter: niceBytes, - }, - { - id: 17, - title: "Internode Data Transfer", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - - yAxisFormatter: niceBytes, - xAxisFormatter: getTimeFromTimestamp, - }, - { - id: 73, - title: "Node IO", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - - yAxisFormatter: niceBytes, - xAxisFormatter: getTimeFromTimestamp, - }, - { - id: 80, - title: "Time Since Last Heal Activity", - data: "N/A", - type: widgetType.simpleWidget, - widgetIcon: , - labelDisplayFunction: niceDaysFromNS, - }, - { - id: 81, - title: "Time Since Last Scan Activity", - data: "N/A", - type: widgetType.simpleWidget, - widgetIcon: , - labelDisplayFunction: niceDaysFromNS, - }, - { - id: 71, - title: "API Request Error Rate", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - - xAxisFormatter: getTimeFromTimestamp, - }, - { - id: 76, - title: "Node Memory Usage", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - - xAxisFormatter: getTimeFromTimestamp, - yAxisFormatter: niceBytes, - }, - { - id: 74, - title: "Drive Used Capacity", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - - xAxisFormatter: getTimeFromTimestamp, - yAxisFormatter: niceBytes, - }, - { - id: 82, - title: "Drives Free Inodes", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - - disableYAxis: true, - xAxisFormatter: getTimeFromTimestamp, - }, - { - id: 11, - title: "Node Syscalls", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - yAxisFormatter: roundNumber, - xAxisFormatter: getTimeFromTimestamp, - }, - { - id: 8, - title: "Node File Descriptors", - data: [], - widgetConfiguration: [ - { - dataKey: "", - keyLabel: "", - lineColor: "#000", - fillColor: "#000", - }, - ], - type: widgetType.linearGraph, - yAxisFormatter: roundNumber, - xAxisFormatter: getTimeFromTimestamp, - }, - { - id: 500, - mergedPanels: [ - { - id: 53, - title: "Online", - data: "N/A", - type: widgetType.singleValue, - }, - { - id: 69, - title: "Offline", - data: "N/A", - type: widgetType.singleValue, - }, - ], - title: "Servers", - }, - { - id: 501, - mergedPanels: [ - { - id: 9, - title: "Online", - data: "N/A", - type: widgetType.singleValue, - }, - { - id: 78, - title: "Offline", - data: "N/A", - type: widgetType.singleValue, - }, - ], - title: "Drives", - }, - { - id: 502, - mergedPanels: [ - { - id: 65, - title: "Upload", - data: "N/A", - type: widgetType.singleValue, - - labelDisplayFunction: niceBytes, - }, - { - id: 64, - title: "Download", - data: "N/A", - type: widgetType.singleValue, - - labelDisplayFunction: niceBytes, - }, - ], - title: "Network", - }, -]; - -const calculateMainValue = (elements: any[], metricCalc: string) => { - if (elements.length === 0) { - return ["", "0"]; - } - - switch (metricCalc) { - case "mean": - const sumValues = elements.reduce((accumulator, currValue) => { - return accumulator + parseFloat(currValue[1]); - }, 0); - - const mean = Math.floor(sumValues / elements.length); - - return ["", mean.toString()]; - default: - const sortResult = elements.sort( - (value1: any[], value2: any[]) => value1[0] - value2[0], - ); - - return sortResult[sortResult.length - 1]; - } -}; - -const constructLabelNames = (metrics: any, legendFormat: string) => { - const keysToReplace = Object.keys(metrics); - const expToReplace = new RegExp(`{{(${keysToReplace.join("|")})}}`, "g"); - - let replacedLegend = legendFormat.replace(expToReplace, (matchItem) => { - const nwMatchItem = matchItem.replace(/({{|}})/g, ""); - return metrics[nwMatchItem]; - }); - - const countVarsOpen = (replacedLegend.match(/{{/g) || []).length; - const countVarsClose = (replacedLegend.match(/}}/g) || []).length; - - let cleanLegend = replacedLegend.replace(/{{(.*?)}}/g, ""); - - if ( - countVarsOpen === countVarsClose && - countVarsOpen !== 0 && - countVarsClose !== 0 - ) { - keysToReplace.forEach((element) => { - replacedLegend = replacedLegend.replace(element, metrics[element]); - }); - - cleanLegend = replacedLegend; - } - - // In case not all the legends were replaced, we remove the placeholders. - return cleanLegend; -}; - -export const widgetDetailsToPanel = ( - payloadData: any, - panelItem: IDashboardPanel, -) => { - if (!payloadData) { - return panelItem; - } - - const typeOfPayload = payloadData.type; - - switch (panelItem.type) { - case widgetType.singleValue: - case widgetType.simpleWidget: - if (typeOfPayload === "stat" || typeOfPayload === "singlestat") { - // We sort values & get the last value - let elements = get(payloadData, "targets[0].result[0].values", []); - - if (elements === null) { - elements = []; - } - - const metricCalc = get( - payloadData, - "options.reduceOptions.calcs[0]", - "lastNotNull", - ); - - const valueDisplay = calculateMainValue(elements, metricCalc); - - const data = panelItem.labelDisplayFunction - ? panelItem.labelDisplayFunction(valueDisplay[1]) - : valueDisplay[1]; - - return { - ...panelItem, - data, - }; - } - break; - case widgetType.pieChart: - if (typeOfPayload === "gauge") { - const metricCalc = get( - payloadData, - "options.reduceOptions.calcs[0]", - "lastNotNull", - ); - - let chartSeries = get(payloadData, "targets", []).filter( - (seriesItem: any) => seriesItem !== null, - ); - - const values = chartSeries.map((chartTarget: any) => { - const resultMap = - chartTarget.result && Array.isArray(chartTarget.result) - ? chartTarget.result - : []; - - const values = resultMap.map((elementValue: any) => { - const values = get(elementValue, "values", []); - const metricKeyItem = Object.keys(elementValue.metric); - const sortResult = values.sort( - (value1: any[], value2: any[]) => - parseInt(value1[0][1]) - parseInt(value2[0][1]), - ); - - const metricName = elementValue.metric[metricKeyItem[0]]; - const value = sortResult[sortResult.length - 1]; - return { - name: metricName, - value: parseInt(value[1]), - legend: chartTarget.legendFormat, - }; - }); - - return values; - }); - - const firstTarget = - chartSeries[0].result && chartSeries[0].result.length > 0 - ? chartSeries[0].result[0].values - : []; - - const totalValues = calculateMainValue(firstTarget, metricCalc); - - const innerLabel = panelItem.labelDisplayFunction - ? panelItem.labelDisplayFunction(totalValues[1]) - : totalValues[1]; - - return { - ...panelItem, - data: values, - innerLabel, - }; - } - break; - case widgetType.linearGraph: - case widgetType.areaGraph: - if (typeOfPayload === "graph") { - let targets = get(payloadData, "targets", []); - if (targets === null) { - targets = []; - } - - const series: any[] = []; - const plotValues: any[] = []; - - targets.forEach( - ( - targetMaster: { legendFormat: string; result: any[] }, - index: number, - ) => { - // Add a new serie to plot variables in case it is not from multiple values - let results = get(targetMaster, "result", []); - const legendFormat = targetMaster.legendFormat; - if (results === null) { - results = []; - } - - results.forEach((itemVals: { metric: object; values: any[] }) => { - // Label Creation - const labelName = constructLabelNames( - itemVals.metric, - legendFormat, - ); - const keyName = `key_${index}${labelName}`; - - // series creation with recently created label - series.push({ - dataKey: keyName, - keyLabel: labelName, - lineColor: "", - fillColor: "", - }); - - // we iterate over values and create elements - let values = get(itemVals, "values", []); - if (values === null) { - values = []; - } - - values.forEach((valInfo: any[]) => { - const itemIndex = plotValues.findIndex( - (element) => element.name === valInfo[0], - ); - - // Element not exists yet - if (itemIndex === -1) { - let itemToPush: any = { name: valInfo[0] }; - itemToPush[keyName] = valInfo[1]; - - plotValues.push(itemToPush); - } else { - plotValues[itemIndex][keyName] = valInfo[1]; - } - }); - }); - }, - ); - - const sortedSeries = series.sort((series1: any, series2: any) => { - if (series1.keyLabel < series2.keyLabel) { - return -1; - } - if (series1.keyLabel > series2.keyLabel) { - return 1; - } - return 0; - }); - - const seriesWithColors = sortedSeries.map( - (serialC: any, index: number) => { - return { - ...serialC, - lineColor: colorsMain[index] || textToRGBColor(serialC.keyLabel), - fillColor: colorsMain[index] || textToRGBColor(serialC.keyLabel), - }; - }, - ); - - const sortedVals = plotValues.sort( - (value1: any, value2: any) => value1.name - value2.name, - ); - - return { - ...panelItem, - widgetConfiguration: seriesWithColors, - data: sortedVals, - }; - } - break; - case widgetType.barChart: - if (typeOfPayload === "bargauge") { - let chartBars = get(payloadData, "targets[0].result", []); - - if (chartBars === null) { - chartBars = []; - } - - const sortFunction = (value1: any[], value2: any[]) => - value1[0] - value2[0]; - - let values = []; - if (panelItem.customStructure) { - values = panelItem.customStructure.map((structureItem) => { - const metricTake = chartBars.find((element: any) => { - return element.metric.range === structureItem.originTag; - }); - - const elements = get(metricTake, "values", []); - - const sortResult = elements.sort(sortFunction); - const lastValue = sortResult[sortResult.length - 1] || ["", "0"]; - - return { - name: structureItem.displayTag, - a: parseInt(lastValue[1]), - }; - }); - } else { - // If no configuration is set, we construct the series for bar chart and return the element - values = chartBars.map((elementValue: any) => { - const metricKeyItem = Object.keys(elementValue.metric); - - const metricName = elementValue.metric[metricKeyItem[0]]; - - const elements = get(elementValue, "values", []); - - const sortResult = elements.sort(sortFunction); - const lastValue = sortResult[sortResult.length - 1] || ["", "0"]; - return { name: metricName, a: parseInt(lastValue[1]) }; - }); - } - - return { - ...panelItem, - data: values, - }; - } - break; - case widgetType.singleRep: - if (typeOfPayload === "stat") { - // We sort values & get the last value - let elements = get(payloadData, "targets[0].result[0].values", []); - if (elements === null) { - elements = []; - } - const metricCalc = get( - payloadData, - "options.reduceOptions.calcs[0]", - "lastNotNull", - ); - - const valueDisplay = calculateMainValue(elements, metricCalc); - - const sortResult = elements.sort( - (value1: any[], value2: any[]) => value1[0] - value2[0], - ); - - let valuesForBackground = []; - - if (sortResult.length === 1) { - valuesForBackground.push({ value: 0 }); - } - - sortResult.forEach((eachVal: any) => { - valuesForBackground.push({ value: parseInt(eachVal[1]) }); - }); - - const innerLabel = panelItem.labelDisplayFunction - ? panelItem.labelDisplayFunction(valueDisplay[1]) - : valueDisplay[1]; - - return { - ...panelItem, - data: valuesForBackground, - innerLabel, - }; - } - break; - } - - return panelItem; -}; - -const verifyNumeric = (item: string) => { - return !isNaN(parseFloat(item)); -}; - -export const splitSizeMetric = (val: string) => { - const splittedText = val.split(" "); - // Value is not a size metric, we return as common string - - const singleValue = () => { - let vl = val; - - if (verifyNumeric(val)) { - vl = representationNumber(parseFloat(val)); - } - return {vl}; - }; - - if (splittedText.length !== 2) { - return singleValue(); - } - - if (!units.includes(splittedText[1])) { - return singleValue(); - } - - return ( - - {splittedText[0]} - {splittedText[1]} - - ); -}; diff --git a/web-app/src/screens/Console/Dashboard/Prometheus/widgetUtils.tsx b/web-app/src/screens/Console/Dashboard/Prometheus/widgetUtils.tsx deleted file mode 100644 index a8574b332e..0000000000 --- a/web-app/src/screens/Console/Dashboard/Prometheus/widgetUtils.tsx +++ /dev/null @@ -1,159 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import { IDashboardPanel, widgetType } from "./types"; -import BarChartWidget from "./Widgets/BarChartWidget"; -import LinearGraphWidget from "./Widgets/LinearGraphWidget"; -import PieChartWidget from "./Widgets/PieChartWidget"; -import SimpleWidget from "./Widgets/SimpleWidget"; -import SingleRepWidget from "./Widgets/SingleRepWidget"; -import SingleValueWidget from "./Widgets/SingleValueWidget"; -import CapacityItem from "./Widgets/CapacityItem"; -import DashboardItemBox from "../DashboardItemBox"; -import HealActivityRenderer, { - SimpleWidgetRenderProps, -} from "./Widgets/HealActivityRenderer"; -import ScanActivityRenderer from "./Widgets/ScanActivityRenderer"; -import UptimeActivityRenderer from "./Widgets/UptimeActivityRenderer"; - -export const componentToUse = ( - value: IDashboardPanel, - timeStart: any, - timeEnd: any, - loading: boolean, - apiPrefix: string, - zoomActivated: boolean = false, -) => { - switch (value.type) { - case widgetType.singleValue: - return ( - - ); - case widgetType.simpleWidget: - let renderFn; - let CmpToRender: any = null; - if (value.id === 80) { - CmpToRender = HealActivityRenderer; - } else if (value.id === 81) { - CmpToRender = ScanActivityRenderer; - } else if (value.id === 1) { - CmpToRender = UptimeActivityRenderer; - } - - if ([80, 81, 1].includes(value.id)) { - renderFn = ({ - valueToRender, - loading, - title, - id, - iconWidget, - }: SimpleWidgetRenderProps) => { - return ( - - ); - }; - } - return ( - - ); - case widgetType.pieChart: - if (value.id === 50) { - return ( - - - - ); - } - return ( - - ); - case widgetType.linearGraph: - case widgetType.areaGraph: - return ( - - ); - case widgetType.barChart: - return ( - - ); - case widgetType.singleRep: - const fillColor = value.fillColor ? value.fillColor : value.color; - return ( - - ); - default: - return null; - } -}; diff --git a/web-app/src/screens/Console/Dashboard/TimeStatItem.tsx b/web-app/src/screens/Console/Dashboard/TimeStatItem.tsx deleted file mode 100644 index d768b18cd9..0000000000 --- a/web-app/src/screens/Console/Dashboard/TimeStatItem.tsx +++ /dev/null @@ -1,73 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2022 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React from "react"; -import styled from "styled-components"; -import get from "lodash/get"; -import { Box, Loader, SuccessIcon } from "mds"; - -const TimeStatBase = styled.div(({ theme }) => ({ - display: "grid", - alignItems: "center", - gap: 8, - height: 33, - paddingLeft: 15, - gridTemplateColumns: "20px 1.5fr .5fr 20px", - background: get(theme, "boxBackground", "#FBFAFA"), // #EBF9EE - "& .min-icon": { - height: "12px", - width: "12px", - fill: get(theme, "signalColors.good", "#4CCB92"), - }, - "& .ok-icon": { - height: "8px", - width: "8px", - fill: get(theme, "signalColors.good", "#4CCB92"), - color: get(theme, "signalColors.good", "#4CCB92"), - }, - "& .timeStatLabel": { - fontSize: "12px", - color: get(theme, "signalColors.good", "#4CCB92"), - fontWeight: 600, - }, - "& .timeStatValue": { - fontSize: "12px", - color: get(theme, "signalColors.good", "#4CCB92"), - }, -})); - -const TimeStatItem = ({ - icon, - label, - value, - loading = false, -}: { - icon: any; - label: any; - value: string; - loading?: boolean; -}) => { - return ( - - {loading ? : icon} - {label} - {value} - {value !== "n/a" ? : null} - - ); -}; - -export default TimeStatItem; diff --git a/web-app/src/screens/Console/Dashboard/dashboardSlice.ts b/web-app/src/screens/Console/Dashboard/dashboardSlice.ts deleted file mode 100644 index fab1c4c5db..0000000000 --- a/web-app/src/screens/Console/Dashboard/dashboardSlice.ts +++ /dev/null @@ -1,72 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2022 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { zoomState } from "./types"; -import { IDashboardPanel } from "./Prometheus/types"; -import { getUsageAsync } from "./dashboardThunks"; -import { AdminInfoResponse } from "api/consoleApi"; - -interface DashboardState { - zoom: zoomState; - usage: AdminInfoResponse | null; - status: "idle" | "loading" | "failed"; - widgetLoadVersion: number; -} - -const initialState: DashboardState = { - status: "idle", - zoom: { - openZoom: false, - widgetRender: null, - }, - usage: null, - widgetLoadVersion: 0, -}; -const dashboardSlice = createSlice({ - name: "dashboard", - initialState, - reducers: { - openZoomPage: (state, action: PayloadAction) => { - state.zoom.openZoom = true; - state.zoom.widgetRender = action.payload; - }, - closeZoomPage: (state) => { - state.zoom.openZoom = false; - state.zoom.widgetRender = null; - }, - reloadWidgets: (state) => { - state.widgetLoadVersion++; - }, - }, - extraReducers: (builder) => { - builder - .addCase(getUsageAsync.pending, (state) => { - state.status = "loading"; - }) - .addCase(getUsageAsync.rejected, (state) => { - state.status = "failed"; - }) - .addCase(getUsageAsync.fulfilled, (state, action) => { - state.status = "idle"; - state.usage = action.payload; - }); - }, -}); -export const { openZoomPage, closeZoomPage, reloadWidgets } = - dashboardSlice.actions; - -export default dashboardSlice.reducer; diff --git a/web-app/src/screens/Console/Dashboard/types.ts b/web-app/src/screens/Console/Dashboard/types.ts deleted file mode 100644 index 37bbcc456a..0000000000 --- a/web-app/src/screens/Console/Dashboard/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import { IDashboardPanel } from "./Prometheus/types"; - -export interface zoomState { - openZoom: boolean; - widgetRender: null | IDashboardPanel; -} diff --git a/web-app/src/screens/Console/EventDestinations/AddEventDestination.tsx b/web-app/src/screens/Console/EventDestinations/AddEventDestination.tsx deleted file mode 100644 index 4c8b35cb44..0000000000 --- a/web-app/src/screens/Console/EventDestinations/AddEventDestination.tsx +++ /dev/null @@ -1,209 +0,0 @@ -// This file is part of MinIO Console Server -// Copyright (c) 2021 MinIO, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -import React, { Fragment, useCallback, useEffect, useState } from "react"; -import get from "lodash/get"; -import { BackLink, Button, FormLayout, Grid, InputBox, PageLayout } from "mds"; -import { useNavigate, useParams } from "react-router-dom"; -import { api } from "api"; -import { errorToHandler } from "api/errors"; -import { - destinationList, - notificationEndpointsFields, - notifyMysql, - notifyPostgres, - removeEmptyFields, -} from "./utils"; -import { IElementValue } from "../Configurations/types"; -import { IAM_PAGES } from "../../../common/SecureComponent/permissions"; -import { - setErrorSnackMessage, - setHelpName, - setServerNeedsRestart, -} from "../../../systemSlice"; -import { useAppDispatch } from "../../../store"; -import { setDestinationLoading } from "./destinationsSlice"; -import withSuspense from "../Common/Components/withSuspense"; -import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper"; -import TargetTitle from "./TargetTitle"; -import HelpMenu from "../HelpMenu"; - -const ConfMySql = withSuspense( - React.lazy(() => import("./CustomForms/ConfMySql")), -); - -const ConfTargetGeneric = withSuspense( - React.lazy(() => import("./ConfTargetGeneric")), -); - -const ConfPostgres = withSuspense( - React.lazy(() => import("./CustomForms/ConfPostgres")), -); - -interface IAddNotificationEndpointProps { - saveAndRefresh: any; -} - -const AddEventDestination = ({ - saveAndRefresh, -}: IAddNotificationEndpointProps) => { - const dispatch = useAppDispatch(); - const navigate = useNavigate(); - const params = useParams(); - - //Local States - const [valuesArr, setValueArr] = useState([]); - const [identifier, setIdentifier] = useState(""); - const [saving, setSaving] = useState(false); - const service = params.service || ""; - - //Effects - useEffect(() => { - if (saving) { - const payload = { - key_values: removeEmptyFields(valuesArr), - }; - api.configs - .setConfig(`${service}:${identifier}`, payload) - .then(() => { - setSaving(false); - dispatch(setServerNeedsRestart(true)); - dispatch(setDestinationLoading(true)); - navigate(IAM_PAGES.EVENT_DESTINATIONS); - }) - .catch((err) => { - setSaving(false); - dispatch(setErrorSnackMessage(errorToHandler(err.error))); - }); - } - }, [ - saving, - service, - valuesArr, - saveAndRefresh, - dispatch, - navigate, - identifier, - ]); - - //Fetch Actions - const submitForm = (event: React.FormEvent) => { - event.preventDefault(); - setSaving(true); - }; - - const onValueChange = useCallback( - (newValue: IElementValue[]) => { - setValueArr(newValue); - }, - [setValueArr], - ); - - let srvComponent; - switch (service) { - case notifyPostgres: { - srvComponent = ; - break; - } - case notifyMysql: { - srvComponent = ; - break; - } - default: { - const fields = get(notificationEndpointsFields, service, []); - - srvComponent = ( - - ); - } - } - - const targetElement = destinationList.find( - (element) => element.actionTrigger === service, - ); - - useEffect(() => { - dispatch(setHelpName("add_notification_endpoint")); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - - navigate(IAM_PAGES.EVENT_DESTINATIONS_ADD)} - /> - - } - actions={} - /> - - - - {service !== "" && ( - - - {targetElement && ( - - )} - - - setIdentifier(e.target.value)} - tooltip={"Unique descriptive string for this destination"} - placeholder="Enter Destination Identifier" - required - /> - - {srvComponent} - - - + + + ); +}; + +export default DeleteMultipleButton; \ No newline at end of file diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/MultiObjects/DownloadMultipleButton.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/MultiObjects/DownloadMultipleButton.tsx new file mode 100644 index 0000000000..d1a56a4a05 --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/MultiObjects/DownloadMultipleButton.tsx @@ -0,0 +1,113 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Fragment } from "react"; +import { Button, DownloadIcon, Tooltip } from "mds"; +import { useSelector } from "react-redux"; +import { + IAM_SCOPES, + permissionTooltipHelper, +} from "../../../../../../common/SecureComponent/permissions"; +import { useParams } from "react-router-dom"; +import { hasPermission } from "../../../../../../common/SecureComponent"; +import { getSessionGrantsWildCard } from "../../../../Buckets/ListBuckets/UploadPermissionUtils"; +import { downloadSelected } from "../../../objectBrowserThunks"; +import { AppState, useAppDispatch } from "../../../../../../store"; + +const DeleteObjectButton = () => { + const params = useParams(); + const dispatch = useAppDispatch(); + + const selectedObjects = useSelector( + (state: AppState) => state.objectBrowser.selectedObjects, + ); + const records = useSelector( + (state: AppState) => state.objectBrowser?.records || [], + ); + const sessionGrants = useSelector((state: AppState) => + state.console.session ? state.console.session.permissions || {} : {} + ); + + + const bucketName = params.bucketName || ""; + + let uploadPath = [bucketName]; + const putObjectPermScopes = [ + IAM_SCOPES.S3_PUT_OBJECT, + IAM_SCOPES.S3_PUT_ACTIONS, + ]; + + const pathAsResourceInPolicy = uploadPath.join("/"); + + const sessionGrantWildCards = getSessionGrantsWildCard( + sessionGrants, + pathAsResourceInPolicy, + putObjectPermScopes, + ); + + const canDownload = hasPermission( + [pathAsResourceInPolicy, ...sessionGrantWildCards], + [IAM_SCOPES.S3_GET_OBJECT, IAM_SCOPES.S3_GET_ACTIONS], + ); + + const checkForDelMarker = (): boolean => { + let isObjDelMarker = false; + if (selectedObjects.length === 1) { + let matchingRec = records.find((obj) => { + return obj.name === `${selectedObjects[0]}` && obj.delete_flag; + }); + + isObjDelMarker = !!matchingRec; + } + return isObjDelMarker; + }; + + const isSelObjectDelMarker = checkForDelMarker(); + + const downloadToolTip = + selectedObjects?.length <= 1 + ? "Download Selected" + : ` Download selected objects as Zip. Any Deleted objects in the selection would be skipped from download.`; + + return ( + + + + + + ); +}; + +export default DeleteObjectButton; \ No newline at end of file diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/MultiObjects/PreviewMultipleButton.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/MultiObjects/PreviewMultipleButton.tsx new file mode 100644 index 0000000000..a428f04324 --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/MultiObjects/PreviewMultipleButton.tsx @@ -0,0 +1,132 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Fragment, useEffect, useState } from "react"; +import { Button, EyeIcon, Tooltip } from "mds"; +import { useSelector } from "react-redux"; +import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions"; +import { useParams } from "react-router-dom"; +import { hasPermission } from "../../../../../../common/SecureComponent"; +import { getSessionGrantsWildCard } from "../../../../Buckets/ListBuckets/UploadPermissionUtils"; +import { downloadSelected } from "../../../objectBrowserThunks"; +import { + AllowedPreviews, + previewObjectType, +} from "../../../../Buckets/ListBuckets/Objects/utils"; +import { AppState, useAppDispatch } from "../../../../../../store"; + +const PreviewMultipleButton = () => { + const params = useParams(); + const dispatch = useAppDispatch(); + + const bucketName = params.bucketName || ""; + + const selectedObjects = useSelector( + (state: AppState) => state.objectBrowser.selectedObjects, + ); + const records = useSelector( + (state: AppState) => state.objectBrowser?.records || [], + ); + const metadata = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectMetadata, + ); + const sessionGrants = useSelector((state: AppState) => + state.console.session ? state.console.session.permissions || {} : {} + ); + + + const [canPreviewFile, setCanPreviewFile] = useState(false); + + let uploadPath = [bucketName]; + const putObjectPermScopes = [ + IAM_SCOPES.S3_PUT_OBJECT, + IAM_SCOPES.S3_PUT_ACTIONS, + ]; + + const pathAsResourceInPolicy = uploadPath.join("/"); + + const sessionGrantWildCards = getSessionGrantsWildCard( + sessionGrants, + pathAsResourceInPolicy, + putObjectPermScopes, + ); + + const canDownload = hasPermission( + [pathAsResourceInPolicy, ...sessionGrantWildCards], + [IAM_SCOPES.S3_GET_OBJECT, IAM_SCOPES.S3_GET_ACTIONS], + ); + + useEffect(() => { + if (selectedObjects.length === 1) { + const objectName = selectedObjects[0]; + + let objectType: AllowedPreviews = previewObjectType( + metadata || {}, + objectName, + ); + + if (objectType !== "none" && canDownload) { + setCanPreviewFile(true); + } else { + setCanPreviewFile(false); + } + } else { + setCanPreviewFile(false); + } + }, [selectedObjects, canDownload, metadata]); + + const checkForDelMarker = (): boolean => { + let isObjDelMarker = false; + if (selectedObjects.length === 1) { + let matchingRec = records.find((obj) => { + return obj.name === `${selectedObjects[0]}` && obj.delete_flag; + }); + + isObjDelMarker = !!matchingRec; + } + return isObjDelMarker; + }; + + const isSelObjectDelMarker = checkForDelMarker(); + + return ( + + + + + + ); +}; + +export default PreviewMultipleButton; \ No newline at end of file diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/MultiObjects/ShareMultipleButton.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/MultiObjects/ShareMultipleButton.tsx new file mode 100644 index 0000000000..3a4ef83486 --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/MultiObjects/ShareMultipleButton.tsx @@ -0,0 +1,122 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Fragment, useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { useSelector } from "react-redux"; +import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions"; +import { getSessionGrantsWildCard } from "../../../../Buckets/ListBuckets/UploadPermissionUtils"; +import { hasPermission } from "../../../../../../common/SecureComponent"; +import { Button, ShareIcon, Tooltip } from "mds"; +import { openShare } from "../../../objectBrowserThunks"; +import { AppState, useAppDispatch } from "../../../../../../store"; + +const ShareMultipleButton = () => { + const params = useParams(); + const dispatch = useAppDispatch(); + + const bucketName = params.bucketName || ""; + + const selectedObjects = useSelector( + (state: AppState) => state.objectBrowser.selectedObjects, + ); + const records = useSelector( + (state: AppState) => state.objectBrowser?.records || [], + ); + const metadata = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectMetadata, + ); + const sessionGrants = useSelector((state: AppState) => + state.console.session ? state.console.session.permissions || {} : {} + ); + + + const [canShareFile, setCanShareFile] = useState(false); + + let uploadPath = [bucketName]; + const putObjectPermScopes = [ + IAM_SCOPES.S3_PUT_OBJECT, + IAM_SCOPES.S3_PUT_ACTIONS, + ]; + + const pathAsResourceInPolicy = uploadPath.join("/"); + + const sessionGrantWildCards = getSessionGrantsWildCard( + sessionGrants, + pathAsResourceInPolicy, + putObjectPermScopes, + ); + + const canDownload = hasPermission( + [pathAsResourceInPolicy, ...sessionGrantWildCards], + [IAM_SCOPES.S3_GET_OBJECT, IAM_SCOPES.S3_GET_ACTIONS], + ); + + useEffect(() => { + if (selectedObjects.length === 1) { + const objectName = selectedObjects[0]; + const isPrefix = objectName.endsWith("/"); + + if (canDownload && !isPrefix) { + setCanShareFile(true); + } else { + setCanShareFile(false); + } + } else { + setCanShareFile(false); + } + }, [selectedObjects, canDownload, metadata]); + + const checkForDelMarker = (): boolean => { + let isObjDelMarker = false; + if (selectedObjects.length === 1) { + let matchingRec = records.find((obj) => { + return obj.name === `${selectedObjects[0]}` && obj.delete_flag; + }); + + isObjDelMarker = !!matchingRec; + } + return isObjDelMarker; + }; + + const isSelObjectDelMarker = checkForDelMarker(); + + return ( + + + + + + ); +}; + +export default ShareMultipleButton; \ No newline at end of file diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/DeleteObjectButton.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/DeleteObjectButton.tsx new file mode 100644 index 0000000000..41241d487a --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/DeleteObjectButton.tsx @@ -0,0 +1,81 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Fragment } from "react"; +import { ExpandMenuOption, TrashIcon } from "mds"; +import { useSelector } from "react-redux"; +import { SecureComponent } from "../../../../../../common/SecureComponent"; +import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions"; +import { useParams } from "react-router-dom"; +import { + setDeleteObjectOpen, +} from "../../../objectBrowserSlice"; +import { AppState, useAppDispatch } from "../../../../../../store"; +import { safeDecodeURIComponent } from "../../../../../../common/utils"; + +const DeleteObjectButton = () => { + const params = useParams(); + const dispatch = useAppDispatch(); + + const bucketName = params.bucketName || ""; + + const actualInfo = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectInfo, + ); + const selectedVersion = useSelector( + (state: AppState) => state.objectBrowser.selectedVersion, + ); + const selectedInternalPaths = useSelector( + (state: AppState) => state.objectBrowser.selectedInternalPaths, + ); + + const internalPathsDecoded = + safeDecodeURIComponent(selectedInternalPaths || "") || ""; + const allPathData = internalPathsDecoded.split("/"); + const currentItem = allPathData.pop() || ""; + + if (!actualInfo) { + return null; + } + + return ( + + + } + className={"danger"} + onClick={() => { + dispatch(setDeleteObjectOpen(true)); + }} + disabled={selectedVersion === "" && actualInfo.is_delete_marker} + > + {`Delete${selectedVersion !== "" ? " version" : ""}`} + + + + ); +}; + +export default DeleteObjectButton; \ No newline at end of file diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/DownloadObjectButton.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/DownloadObjectButton.tsx new file mode 100644 index 0000000000..5d5a9aaaaa --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/DownloadObjectButton.tsx @@ -0,0 +1,99 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Fragment } from "react"; +import { Button, DownloadIcon, Tooltip } from "mds"; +import { useSelector } from "react-redux"; +import { hasPermission } from "../../../../../../common/SecureComponent"; +import { + IAM_SCOPES, + permissionTooltipHelper, +} from "../../../../../../common/SecureComponent/permissions"; +import { useParams } from "react-router-dom"; +import { downloadObject } from "../../../utils"; +import { AppState, useAppDispatch } from "../../../../../../store"; +import { safeDecodeURIComponent } from "../../../../../../common/utils"; + +interface IDownloadButtonProps { + fullButton?: boolean; +} + +const DownloadObjectButton = ({fullButton} : IDownloadButtonProps) => { + const params = useParams(); + const dispatch = useAppDispatch(); + + const bucketName = params.bucketName || ""; + + const actualInfo = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectInfo + ); + const selectedInternalPaths = useSelector( + (state: AppState) => state.objectBrowser.selectedInternalPaths + ); + + if (!actualInfo || !selectedInternalPaths) { + return null; + } + + const internalPathsDecoded = + safeDecodeURIComponent(selectedInternalPaths) || ""; + const allPathData = internalPathsDecoded.split("/"); + const currentItem = allPathData.pop() || ""; + + const objectResources = [ + bucketName, + currentItem, + [bucketName, actualInfo.name].join("/"), + ]; + + const canGetObject = hasPermission(objectResources, [ + IAM_SCOPES.S3_GET_OBJECT, + IAM_SCOPES.S3_GET_ACTIONS, + ]); + + return ( + + + + + + ); +}; + +export default LegalHoldObjectButton; diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/ObjectVersionsButton.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/ObjectVersionsButton.tsx new file mode 100644 index 0000000000..548b382d55 --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/ObjectVersionsButton.tsx @@ -0,0 +1,126 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Fragment } from "react"; +import { Button, FeatherIcon, Tooltip } from "mds"; +import { useSelector } from "react-redux"; +import { hasPermission } from "../../../../../../common/SecureComponent"; +import { + IAM_SCOPES, + permissionTooltipHelper, +} from "../../../../../../common/SecureComponent/permissions"; +import { useParams } from "react-router-dom"; +import { setVersionsModeEnabled } from "../../../objectBrowserSlice"; +import { AppState, useAppDispatch } from "../../../../../../store"; +import { selDistSet } from "../../../../../../systemSlice"; +import { safeDecodeURIComponent } from "../../../../../../common/utils"; + +const ObjectVersionsButton = () => { + const params = useParams(); + const dispatch = useAppDispatch(); + + const bucketName = params.bucketName || ""; + + const actualInfo = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectInfo, + ); + const selectedInternalPaths = useSelector( + (state: AppState) => state.objectBrowser.selectedInternalPaths, + ); + const versionsMode = useSelector( + (state: AppState) => state.objectBrowser.versionsMode, + ); + const distributedSetup = useSelector(selDistSet); + + const internalPathsDecoded = + safeDecodeURIComponent(selectedInternalPaths || "") || ""; + const allPathData = internalPathsDecoded.split("/"); + const currentItem = allPathData.pop() || ""; + + if (!actualInfo) { + return null; + } + + const objectResources = [ + bucketName, + currentItem, + [bucketName, actualInfo.name].join("/"), + ]; + + const canChangeVersioning = hasPermission(objectResources, [ + IAM_SCOPES.S3_GET_BUCKET_VERSIONING, + IAM_SCOPES.S3_PUT_BUCKET_VERSIONING, + IAM_SCOPES.S3_GET_OBJECT_VERSION, + IAM_SCOPES.S3_GET_ACTIONS, + IAM_SCOPES.S3_PUT_ACTIONS, + ]); + + // calculate object name to display + let objectNameArray: string[] = []; + if (actualInfo && actualInfo.name) { + objectNameArray = actualInfo.name.split("/"); + } + + const objectName = + objectNameArray.length > 0 + ? objectNameArray[objectNameArray.length - 1] + : actualInfo.name; + + return ( + + + + + + ); +}; + +export default ObjectVersionsButton; \ No newline at end of file diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/PreviewObjectButton.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/PreviewObjectButton.tsx new file mode 100644 index 0000000000..27fba8935c --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/PreviewObjectButton.tsx @@ -0,0 +1,110 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Fragment, useState } from "react"; +import { Button, EyeIcon, Tooltip } from "mds"; +import { useSelector } from "react-redux"; +import { + AllowedPreviews, + previewObjectType, +} from "../../../../Buckets/ListBuckets/Objects/utils"; +import { hasPermission } from "../../../../../../common/SecureComponent"; +import { + IAM_SCOPES, + permissionTooltipHelper, +} from "../../../../../../common/SecureComponent/permissions"; +import { useParams } from "react-router-dom"; +import PreviewFileModal from "../../../../Buckets/ListBuckets/Objects/Preview/PreviewFileModal"; +import { AppState } from "../../../../../../store"; +import { safeDecodeURIComponent } from "../../../../../../common/utils"; + +const PreviewObjectButton = () => { + const params = useParams(); + + const [previewOpen, setPreviewOpen] = useState(false); + + const bucketName = params.bucketName || ""; + + const actualInfo = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectInfo, + ); + const metadata = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectMetadata, + ); + const selectedInternalPaths = useSelector( + (state: AppState) => state.objectBrowser.selectedInternalPaths, + ); + + if (!actualInfo || !selectedInternalPaths || !metadata) { + return null; + } + + const internalPathsDecoded = safeDecodeURIComponent(selectedInternalPaths) || ""; + const allPathData = internalPathsDecoded.split("/"); + const currentItem = allPathData.pop() || ""; + + const objectResources = [ + bucketName, + currentItem, + [bucketName, actualInfo.name].join("/"), + ]; + + const canGetObject = hasPermission(objectResources, [ + IAM_SCOPES.S3_GET_OBJECT, + IAM_SCOPES.S3_GET_ACTIONS, + ]); + + let objectType: AllowedPreviews = previewObjectType(metadata, currentItem); + + return ( + + {previewOpen && actualInfo && ( + { + setPreviewOpen(false); + }} + /> + )} + + + + + ); +}; + +export default RetentionObjectButton; \ No newline at end of file diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/ShareObjectButton.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/ShareObjectButton.tsx new file mode 100644 index 0000000000..be900eda9e --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/ActionButtons/SingleObject/ShareObjectButton.tsx @@ -0,0 +1,98 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Fragment, useState } from "react"; +import { Button, ShareIcon, Tooltip } from "mds"; +import { useSelector } from "react-redux"; +import { hasPermission } from "../../../../../../common/SecureComponent"; +import { + IAM_SCOPES, + permissionTooltipHelper, +} from "../../../../../../common/SecureComponent/permissions"; +import { useParams } from "react-router-dom"; +import ShareFile from "../../../../Buckets/ListBuckets/Objects/ObjectDetails/ShareFile"; +import { AppState } from "../../../../../../store"; +import { safeDecodeURIComponent } from "../../../../../../common/utils"; + +const ShareObjectButton = () => { + const params = useParams(); + + const [shareFileModalOpen, setShareFileModalOpen] = useState(false); + + const bucketName = params.bucketName || ""; + + const actualInfo = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectInfo, + ); + const selectedInternalPaths = useSelector( + (state: AppState) => state.objectBrowser.selectedInternalPaths, + ); + + if (!actualInfo || !selectedInternalPaths) { + return null; + } + + const internalPathsDecoded = safeDecodeURIComponent(selectedInternalPaths) || ""; + const allPathData = internalPathsDecoded.split("/"); + const currentItem = allPathData.pop() || ""; + + const objectResources = [ + bucketName, + currentItem, + [bucketName, actualInfo.name].join("/"), + ]; + + const canGetObject = hasPermission(objectResources, [ + IAM_SCOPES.S3_GET_OBJECT, + IAM_SCOPES.S3_GET_ACTIONS, + ]); + + return ( + + {shareFileModalOpen && actualInfo && ( + { + setShareFileModalOpen(false); + }} + bucketName={bucketName} + dataObject={actualInfo} + /> + )} + + + + + ); +}; + +export default TagObjectButton; \ No newline at end of file diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/MenuActionModals/MultiActionModals.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/MenuActionModals/MultiActionModals.tsx new file mode 100644 index 0000000000..aad64399e8 --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/MenuActionModals/MultiActionModals.tsx @@ -0,0 +1,67 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { Fragment, lazy } from "react"; +import { useSelector } from "react-redux"; +import { useParams } from "react-router-dom"; +import { useNotification } from "mds"; +import { AppState, useAppDispatch } from "../../../../../store"; +import { setDeleteMultipleOpen, setReloadObjectsList, setSelectedObjects } from "../../objectBrowserSlice"; +import DeleteMultipleObjects from "../../../Buckets/ListBuckets/Objects/ListObjects/DeleteMultipleObjects"; + +const MultiActionModals = () => { + const dispatch = useAppDispatch(); + const params = useParams(); + const notification = useNotification(); + + const bucketName = params.bucketName || ""; + + const selectedObjects = useSelector( + (state: AppState) => state.objectBrowser.selectedObjects, + ); + const versioningConfig = useSelector( + (state: AppState) => state.objectBrowser.versionInfo, + ); + const deleteMultipleOpen = useSelector( + (state: AppState) => state.objectBrowser.multiObjectModals.deleteMultiple, + ); + + const closeDeleteMultipleModalAndRefresh = (refresh: boolean) => { + dispatch(setDeleteMultipleOpen(false)); + + if (refresh) { + notification.success(`Objects deleted successfully.`); + dispatch(setSelectedObjects([])); + dispatch(setReloadObjectsList(true)); + } + }; + + return ( + + {deleteMultipleOpen && ( + + )} + + ); +}; + +export default MultiActionModals; diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/MenuActionModals/SingleActionModals.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/MenuActionModals/SingleActionModals.tsx new file mode 100644 index 0000000000..4ab1770701 --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/MenuActionModals/SingleActionModals.tsx @@ -0,0 +1,141 @@ +import React, { Fragment } from "react"; +import { useSelector } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { AppState, useAppDispatch } from "../../../../../store"; +import { selDistSet } from "../../../../../systemSlice"; +import { + resetMessages, setDeleteObjectOpen, setInspectOpen, setLegalHoldOpen, setLoadingObjectInfo, + setLoadingVersions, + setObjectInfo, + setObjectMetadata, + setReloadObjectsList, setRetentionOpen, setSelectedVersion +} from "../../objectBrowserSlice"; +import SetLegalHoldModal from "../../../Buckets/ListBuckets/Objects/ObjectDetails/SetLegalHoldModal"; +import InspectObject from "../../../Buckets/ListBuckets/Objects/ListObjects/InspectObject"; +import SetRetention from "../../../Buckets/ListBuckets/Objects/ObjectDetails/SetRetention"; +import DeleteObject from "../../../Buckets/ListBuckets/Objects/ListObjects/DeleteObject"; + + + +const SingleActionModals = () => { + const dispatch = useAppDispatch(); + const navigate = useNavigate(); + + const params = useParams(); + const bucketName = params.bucketName || ""; + + const actualInfo = useSelector( + (state: AppState) => state.objectBrowser.objectData.selectedObjectInfo, + ); + const selectedVersion = useSelector( + (state: AppState) => state.objectBrowser.selectedVersion, + ); + const selectedInternalPaths = useSelector( + (state: AppState) => state.objectBrowser.selectedInternalPaths, + ); + const versioningConfig = useSelector( + (state: AppState) => state.objectBrowser.versionInfo, + ); + const legalHoldOpen = useSelector( + (state: AppState) => state.objectBrowser.singleObjectModals.legalHoldOpen, + ); + const inspectModalOpen = useSelector( + (state: AppState) => state.objectBrowser.singleObjectModals.inspectOpen, + ); + const retentionModalOpen = useSelector( + (state: AppState) => state.objectBrowser.singleObjectModals.retentionOpen, + ); + const deleteOpen = useSelector( + (state: AppState) => + state.objectBrowser.singleObjectModals.deleteObjectOpen, + ); + + const distributedSetup = useSelector(selDistSet); + + const allPathData = (selectedInternalPaths || "").split("/"); + const currentItem = allPathData.pop() || ""; + + /*Modal Close Actions*/ + const closeDeleteModal = (closeAndReload: boolean) => { + dispatch(setDeleteObjectOpen(false)); + + if (closeAndReload && selectedVersion === "") { + dispatch(setObjectInfo(null)); + dispatch(setObjectMetadata(null)); + dispatch(resetMessages()); + dispatch(setReloadObjectsList(true)); + + navigate( + `../${encodeURIComponent(bucketName)}/${encodeURIComponent(`${allPathData.join("/")}/`)}`, + ); + } else { + dispatch(setLoadingVersions(true)); + dispatch(setSelectedVersion("")); + dispatch(setLoadingObjectInfo(true)); + } + }; + + const closeLegalHoldModal = (reload: boolean) => { + dispatch(setLegalHoldOpen(false)); + if (reload) { + dispatch(setLoadingObjectInfo(true)); + } + }; + + const closeInspectModal = (reloadObjectData: boolean) => { + dispatch(setInspectOpen(false)); + if (reloadObjectData) { + dispatch(setLoadingObjectInfo(true)); + } + }; + + const closeRetentionModal = (updateInfo: boolean) => { + dispatch(setRetentionOpen(false)); + if (updateInfo) { + dispatch(setLoadingObjectInfo(true)); + } + }; + + return ( + + {legalHoldOpen && actualInfo && ( + + )} + {inspectModalOpen && actualInfo && ( + + )} + {retentionModalOpen && actualInfo && ( + + )} + {deleteOpen && ( + + )} + + ); +}; + +export default SingleActionModals; diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/MultipleObjectSelection.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/MultipleObjectSelection.tsx new file mode 100644 index 0000000000..85f579f8f8 --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/MultipleObjectSelection.tsx @@ -0,0 +1,84 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { useSelector } from "react-redux"; +import { + Box, + Button, + ButtonGroup, + EllipsisVerticalIcon, + ExpandMenu, + XIcon, +} from "mds"; + +import AnonymousMultipleButton from "./ActionButtons/MultiObjects/AnonymousMultipeButton"; +import DeleteMultipleButton from "./ActionButtons/MultiObjects/DeleteMultipleButton"; +import DownloadMultipleButton from "./ActionButtons/MultiObjects/DownloadMultipleButton"; +import PreviewMultipleButton from "./ActionButtons/MultiObjects/PreviewMultipleButton"; +import ShareMultipleButton from "./ActionButtons/MultiObjects/ShareMultipleButton"; +import { AppState, useAppDispatch } from "../../../../store"; +import MultiActionModals from "./MenuActionModals/MultiActionModals"; +import { setSelectedObjects } from "../objectBrowserSlice"; +import MultipleBase from "../../../../common/MultipleBase"; + +const MultipleObjectSelection = () => { + const dispatch = useAppDispatch(); + const selectedObjects = useSelector( + (state: AppState) => state.objectBrowser.selectedObjects, + ); + + return ( + + + + + + + {selectedObjects.length} Object + {selectedObjects.length === 1 ? "" : "s"} selected + + + + + + + + } + dropMenuPosition={"end"} + dropArrow={false} + > + + + + + + ); +}; + +export default MultipleObjectSelection; diff --git a/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/SingleObjectSelected.tsx b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/SingleObjectSelected.tsx new file mode 100644 index 0000000000..cd8105755a --- /dev/null +++ b/web-app/src/screens/Console/ObjectBrowser/ButtonGroups/SingleObjectSelected.tsx @@ -0,0 +1,76 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { Box, ButtonGroup, EllipsisVerticalIcon, ExpandMenu, ExpandMenuDivider } from "mds"; + +import DeleteObjectButton from "./ActionButtons/SingleObject/DeleteObjectButton"; +import DownloadObjectButton from "./ActionButtons/SingleObject/DownloadObjectButton"; +import InspectObjectButton from "./ActionButtons/SingleObject/InspectObjectButton"; +import LegalHoldObjectButton from "./ActionButtons/SingleObject/LegalHoldObjectButton"; +import ObjectVersionsButton from "./ActionButtons/SingleObject/ObjectVersionsButton"; +import PreviewObjectButton from "./ActionButtons/SingleObject/PreviewObjectButton"; +import RetentionObjectButton from "./ActionButtons/SingleObject/RetentionObjectButton"; +import ShareObjectButton from "./ActionButtons/SingleObject/ShareObjectButton"; +import SingleActionModals from "./MenuActionModals/SingleActionModals"; + +const SingleObjectSelected = () => { + return ( + + + span, > button": { + flexGrow: 1, + display: "flex", + justifyContent: "center", + alignItems: "center", + "& button": { + width: "100%", + }, + }, + }} + + > + + + + + } + dropMenuPosition={"end"} + dropArrow={false} + compact + > + + + + + + + + + ); +}; + +export default SingleObjectSelected; diff --git a/web-app/src/screens/Console/ObjectBrowser/FilterObjectsSB.tsx b/web-app/src/screens/Console/ObjectBrowser/FilterObjectsSB.tsx index e2303aad85..8d5a93eb6f 100644 --- a/web-app/src/screens/Console/ObjectBrowser/FilterObjectsSB.tsx +++ b/web-app/src/screens/Console/ObjectBrowser/FilterObjectsSB.tsx @@ -28,11 +28,12 @@ const FilterObjectsSB = () => { ); return ( { dispatch(setSearchObjects(value)); }} value={searchObjects} + sx={{width: "initial"}} /> ); }; diff --git a/web-app/src/screens/Console/ObjectBrowser/OBBucketList.tsx b/web-app/src/screens/Console/ObjectBrowser/OBBucketList.tsx index 8e2d53624e..2299482bc9 100644 --- a/web-app/src/screens/Console/ObjectBrowser/OBBucketList.tsx +++ b/web-app/src/screens/Console/ObjectBrowser/OBBucketList.tsx @@ -18,16 +18,16 @@ import React, { Fragment, useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { - ActionLink, - BucketsIcon, + LinkButton, + BucketIcon, Button, DataTable, HelpBox, PageLayout, ProgressBar, - RefreshIcon, + RefreshCWIcon, Grid, - HelpTip, + Tooltip, } from "mds"; import { actionsTray } from "../Common/FormComponents/common/styleLibrary"; import { SecureComponent } from "../../../common/SecureComponent"; @@ -51,7 +51,6 @@ import { Bucket } from "../../../api/consoleApi"; import { api } from "../../../api"; import { errorToHandler } from "../../../api/errors"; import HelpMenu from "../HelpMenu"; -import { usageClarifyingContent } from "../Dashboard/BasicDashboard/ReportedUsage"; const OBListBuckets = () => { const dispatch = useAppDispatch(); @@ -158,8 +157,8 @@ const OBListBuckets = () => { onClick={() => { setLoading(true); }} - icon={} - variant={"regular"} + icon={} + variant={"secondary"} /> @@ -191,7 +190,7 @@ const OBListBuckets = () => { elementKey: "name", renderFunction: (label) => (
- { { label: "Objects", elementKey: "objects", - renderFunction: (size: number | null) => + renderFunction: (size: number | undefined) => size ? size.toLocaleString() : 0, }, { label: "Size", elementKey: "size", - renderFunction: (size: number) => ( + renderFunction: (size: number | undefined) => (
setClickOverride(true)} onMouseLeave={() => setClickOverride(false)} > - - {niceBytesInt(size || 0)} - + <>{niceBytesInt(size || 0)}
), }, { label: "Access", elementKey: "rw_access", - renderFullObject: true, - renderFunction: (bucket: Bucket) => { + renderFunction: (bucket: any) => { let access = []; if (bucket.rw_access?.read) { access.push("R"); @@ -261,7 +254,7 @@ const OBListBuckets = () => { > } + icon={} title={"No Results"} help={ @@ -283,7 +276,7 @@ const OBListBuckets = () => { > } + icon={} title={"Buckets"} help={ @@ -312,13 +305,13 @@ const OBListBuckets = () => { >
To get started,  - { navigate(IAM_PAGES.ADD_BUCKETS); }} > Create a Bucket. - +
} diff --git a/web-app/src/screens/Console/ObjectBrowser/OBHeader.tsx b/web-app/src/screens/Console/ObjectBrowser/OBHeader.tsx index 62b38abad7..fe47d3727c 100644 --- a/web-app/src/screens/Console/ObjectBrowser/OBHeader.tsx +++ b/web-app/src/screens/Console/ObjectBrowser/OBHeader.tsx @@ -91,7 +91,7 @@ const OBHeader = ({ bucketName }: IOBHeader) => { resource={bucketName} errorProps={{ disabled: true }} > - +
adf
) : ( @@ -114,63 +114,12 @@ const OBHeader = ({ bucketName }: IOBHeader) => { return ( - {!obOnly ? ( - { - navigate(IAM_PAGES.OBJECT_BROWSER_VIEW); - }} - /> - } - actions={ - - - -