diff --git a/portal-ui/src/screens/Console/Common/FormComponents/ModalError/ModalError.tsx b/portal-ui/src/screens/Console/Common/FormComponents/ModalError/ModalError.tsx index aa36ed784..668ddc7ce 100644 --- a/portal-ui/src/screens/Console/Common/FormComponents/ModalError/ModalError.tsx +++ b/portal-ui/src/screens/Console/Common/FormComponents/ModalError/ModalError.tsx @@ -42,7 +42,6 @@ import { setErrorSnackMessage } from "../../../../../actions"; import { snackBarMessage } from "../../../../../types"; import { setModalErrorSnackMessage, - setModalSnackMessage, } from "../../../../../actions"; interface ImodalErrorProps { diff --git a/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts b/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts index 3dccb76e9..0d5455a51 100644 --- a/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts +++ b/portal-ui/src/screens/Console/Common/FormComponents/common/styleLibrary.ts @@ -431,6 +431,8 @@ export const widgetCommon = { borderBottom: "#eef1f4 1px solid", paddingBottom: 14, marginBottom: 5, + display: "flex" as const, + justifyContent: "space-between" as const, }, contentContainer: { justifyContent: "center" as const, @@ -470,6 +472,26 @@ export const widgetCommon = { overflow: "hidden" as const, textOverflow: "ellipsis" as const, }, + zoomChartCont: { + position: "relative" as const, + height: 340, + width: "100%", + }, + zoomChartIcon: { + backgroundColor: "transparent", + border: 0, + padding: 0, + cursor: "pointer", + "& svg": { + color: "#D0D0D0", + height: 16, + }, + "&:hover": { + "& svg": { + color: "#404143", + }, + }, + }, }; export const widgetContainerCommon = { diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx index 05dac639c..1af8909e4 100644 --- a/portal-ui/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx +++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/PrDashboard.tsx @@ -25,27 +25,26 @@ import { actionsTray, widgetContainerCommon, } from "../../Common/FormComponents/common/styleLibrary"; -import { IDashboardPanel, widgetType } from "./types"; +import { IDashboardPanel } from "./types"; import { getWidgetsWithValue, panelsConfiguration } from "./utils"; import { TabPanel } from "../../../shared/tabs"; import { ErrorResponseHandler } from "../../../../common/types"; import { setErrorSnackMessage } from "../../../../actions"; -import SingleValueWidget from "./Widgets/SingleValueWidget"; -import LinearGraphWidget from "./Widgets/LinearGraphWidget"; -import BarChartWidget from "./Widgets/BarChartWidget"; -import PieChartWidget from "./Widgets/PieChartWidget"; -import SingleRepWidget from "./Widgets/SingleRepWidget"; import DateTimePickerWrapper from "../../Common/FormComponents/DateTimePickerWrapper/DateTimePickerWrapper"; import api from "../../../../common/api"; import SyncIcon from "../../../../icons/SyncIcon"; import TabSelector from "../../Common/TabSelector/TabSelector"; -import SimpleWidget from "./Widgets/SimpleWidget"; import MergedWidgets from "./MergedWidgets"; +import { componentToUse } from "./widgetUtils"; +import ZoomWidget from "./ZoomWidget"; +import { AppState } from "../../../../store"; interface IPrDashboard { classes: any; displayErrorMessage: typeof setErrorSnackMessage; apiPrefix?: string; + zoomOpen: boolean; + zoomWidget: null | IDashboardPanel; } const styles = (theme: Theme) => @@ -82,6 +81,8 @@ const PrDashboard = ({ classes, displayErrorMessage, apiPrefix = "admin", + zoomOpen, + zoomWidget, }: IPrDashboard) => { const [timeStart, setTimeStart] = useState(null); const [timeEnd, setTimeEnd] = useState(null); @@ -92,88 +93,6 @@ const PrDashboard = ({ const panels = useCallback( (tabName: string, filterPanels?: number[][] | null) => { - const componentToUse = (value: IDashboardPanel, index: number) => { - switch (value.type) { - case widgetType.singleValue: - return ( - - ); - case widgetType.simpleWidget: - return ( - - ); - case widgetType.pieChart: - 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; - } - }; - return filterPanels?.map((panelLine, indexLine) => { const totalPanelsContained = panelLine.length; @@ -216,16 +135,28 @@ const PrDashboard = ({ title={panelInfo.title} leftComponent={componentToUse( panelInfo.mergedPanels[0], - 0 + timeStart, + timeEnd, + loading, + apiPrefix )} rightComponent={componentToUse( panelInfo.mergedPanels[1], - 1 + timeStart, + timeEnd, + loading, + apiPrefix )} /> ) : ( - componentToUse(panelInfo, indexPanel) + componentToUse( + panelInfo, + timeStart, + timeEnd, + loading, + apiPrefix + ) )} ) : null} @@ -313,6 +244,16 @@ const PrDashboard = ({ return ( + {zoomOpen && ( + + )} ); }; -/* -< -*/ -const connector = connect(null, { +const mapState = (state: AppState) => ({ + zoomOpen: state.dashboard.zoom.openZoom, + zoomWidget: state.dashboard.zoom.widgetRender, +}); + +const connector = connect(mapState, { displayErrorMessage: setErrorSnackMessage, }); diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/BarChartWidget.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/BarChartWidget.tsx index 102fa3588..5cf5d23f8 100644 --- a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/BarChartWidget.tsx +++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/BarChartWidget.tsx @@ -28,6 +28,7 @@ import { import { MaterialUiPickersDate } from "@material-ui/pickers/typings/date"; import { CircularProgress } from "@material-ui/core"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; +import ZoomOutMapIcon from "@material-ui/icons/ZoomOutMap"; import { IBarChartConfiguration } from "./types"; import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary"; import BarChartTooltip from "./tooltips/BarChartTooltip"; @@ -36,6 +37,7 @@ import { IDashboardPanel } from "../types"; import { widgetDetailsToPanel } from "../utils"; import { ErrorResponseHandler } from "../../../../../common/types"; import api from "../../../../../common/api"; +import { openZoomPage } from "../../actions"; interface IBarChartWidget { classes: any; @@ -46,6 +48,8 @@ interface IBarChartWidget { propLoading: boolean; displayErrorMessage: any; apiPrefix: string; + zoomActivated?: boolean; + openZoomPage: typeof openZoomPage; } const styles = (theme: Theme) => @@ -84,6 +88,8 @@ const BarChartWidget = ({ propLoading, displayErrorMessage, apiPrefix, + zoomActivated = false, + openZoomPage, }: IBarChartWidget) => { const [loading, setLoading] = useState(true); const [data, setData] = useState([]); @@ -147,15 +153,31 @@ const BarChartWidget = ({ } return ( -
-
{title}
+
+ {!zoomActivated && ( +
+ {title}{" "} + +
+ )} {loading && (
)} {!loading && ( -
+
{barChartConfiguration.length === 1 ? ( @@ -214,6 +236,7 @@ const BarChartWidget = ({ const connector = connect(null, { displayErrorMessage: setErrorSnackMessage, + openZoomPage: openZoomPage, }); export default withStyles(styles)(connector(BarChartWidget)); diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx index 860b59685..2e65c16aa 100644 --- a/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx +++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/Widgets/LinearGraphWidget.tsx @@ -14,7 +14,7 @@ // 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 React, { Fragment, useEffect, useState } from "react"; import { connect } from "react-redux"; import { Area, @@ -28,6 +28,7 @@ import { import { CircularProgress } from "@material-ui/core"; import { MaterialUiPickersDate } from "@material-ui/pickers/typings/date"; import { createStyles, Theme, withStyles } from "@material-ui/core/styles"; +import ZoomOutMapIcon from "@material-ui/icons/ZoomOutMap"; import { ILinearGraphConfiguration } from "./types"; import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary"; import { IDashboardPanel } from "../types"; @@ -36,6 +37,7 @@ import { widgetDetailsToPanel } from "../utils"; import { ErrorResponseHandler } from "../../../../../common/types"; import api from "../../../../../common/api"; import LineChartTooltip from "./tooltips/LineChartTooltip"; +import { openZoomPage } from "../../actions"; interface ILinearGraphWidget { classes: any; @@ -50,6 +52,8 @@ interface ILinearGraphWidget { yAxisFormatter?: (item: string) => string; xAxisFormatter?: (item: string) => string; areaWidget?: boolean; + zoomActivated?: boolean; + openZoomPage: typeof openZoomPage; } const styles = (theme: Theme) => @@ -61,6 +65,9 @@ const styles = (theme: Theme) => height: "100%", flexGrow: 1, }, + verticalAlignment: { + flexDirection: "column", + }, chartCont: { position: "relative", height: 140, @@ -70,7 +77,7 @@ const styles = (theme: Theme) => display: "flex", flexDirection: "column", flex: "0 1 auto", - height: 130, + maxHeight: 130, margin: 0, overflowY: "auto", position: "relative", @@ -99,6 +106,8 @@ const LinearGraphWidget = ({ areaWidget = false, yAxisFormatter = (item: string) => item, xAxisFormatter = (item: string) => item, + zoomActivated = false, + openZoomPage, }: ILinearGraphWidget) => { const [loading, setLoading] = useState(true); const [data, setData] = useState([]); @@ -174,13 +183,33 @@ const LinearGraphWidget = ({ }; return ( -
-
{title}
-
+
+ {!zoomActivated && ( +
+ {title}{" "} + +
+ )} +
{loading && } {!loading && ( -
+
{!areaWidget && ( -
- {linearConfiguration.map((section, index) => { - return ( -
+ + {zoomActivated && ( + + Series +
+
+
+ )} +
+ {linearConfiguration.map((section, index) => { + return (
-
- {section.keyLabel} + className={classes.singleLegendContainer} + key={`legend-${section.keyLabel}-${index.toString()}`} + > +
+
+ {section.keyLabel} +
-
- ); - })} -
+ ); + })} +
+
)} )} @@ -295,6 +333,7 @@ const LinearGraphWidget = ({ const connector = connect(null, { displayErrorMessage: setErrorSnackMessage, + openZoomPage: openZoomPage, }); export default withStyles(styles)(connector(LinearGraphWidget)); diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/ZoomWidget.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/ZoomWidget.tsx new file mode 100644 index 000000000..1ad7aaed7 --- /dev/null +++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/ZoomWidget.tsx @@ -0,0 +1,66 @@ +// 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 { connect } from "react-redux"; +import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper"; +import { IDashboardPanel } from "./types"; +import { componentToUse } from "./widgetUtils"; +import { closeZoomPage } from "../actions"; + +interface IZoomWidget { + widgetRender: number; + value: IDashboardPanel | null; + modalOpen: boolean; + timeStart: any; + timeEnd: any; + apiPrefix: string; + onCloseAction: typeof closeZoomPage; +} +const ZoomWidget = ({ + value, + modalOpen, + timeStart, + timeEnd, + apiPrefix, + onCloseAction, +}: IZoomWidget) => { + if (!value) { + return null; + } + + return ( + { + onCloseAction(); + }} + modalOpen={modalOpen} + wideLimit={false} + noContentPadding + > + + {componentToUse(value, timeStart, timeEnd, true, apiPrefix, true)} + + + ); +}; + +const connector = connect(null, { + onCloseAction: closeZoomPage, +}); + +export default connector(ZoomWidget); diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/utils.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/utils.tsx index 27cdd538f..dd72b4b17 100644 --- a/portal-ui/src/screens/Console/Dashboard/Prometheus/utils.tsx +++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/utils.tsx @@ -27,7 +27,6 @@ import { } from "../../../../common/utils"; import HealIcon from "../../../../icons/HealIcon"; import DiagnosticsIcon from "../../../../icons/DiagnosticsIcon"; -import HistoryIcon from "../../../../icons/HistoryIcon"; import { UptimeIcon } from "../../../../icons"; const colorsMain = [ diff --git a/portal-ui/src/screens/Console/Dashboard/Prometheus/widgetUtils.tsx b/portal-ui/src/screens/Console/Dashboard/Prometheus/widgetUtils.tsx new file mode 100644 index 000000000..6c2e65684 --- /dev/null +++ b/portal-ui/src/screens/Console/Dashboard/Prometheus/widgetUtils.tsx @@ -0,0 +1,115 @@ +// 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"; + +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: + return ( + + ); + case widgetType.pieChart: + 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/portal-ui/src/screens/Console/Dashboard/actions.ts b/portal-ui/src/screens/Console/Dashboard/actions.ts new file mode 100644 index 000000000..15ccc8299 --- /dev/null +++ b/portal-ui/src/screens/Console/Dashboard/actions.ts @@ -0,0 +1,44 @@ +// 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 const DASHBOARD_OPEN_ZOOM = "DASHBOARD/OPEN_ZOOM"; +export const DASHBOARD_CLOSE_ZOOM = "DASHBOARD/CLOSE_ZOOM"; + +interface OpenChartZoom { + type: typeof DASHBOARD_OPEN_ZOOM; + widget: IDashboardPanel; +} + +interface CloseChartZoom { + type: typeof DASHBOARD_CLOSE_ZOOM; +} + +export type ZoomActionTypes = OpenChartZoom | CloseChartZoom; + +export function openZoomPage(widget: IDashboardPanel) { + return { + type: DASHBOARD_OPEN_ZOOM, + widget, + }; +} + +export function closeZoomPage() { + return { + type: DASHBOARD_CLOSE_ZOOM, + }; +} diff --git a/portal-ui/src/screens/Console/Dashboard/reducer.ts b/portal-ui/src/screens/Console/Dashboard/reducer.ts new file mode 100644 index 000000000..a4b3cfc55 --- /dev/null +++ b/portal-ui/src/screens/Console/Dashboard/reducer.ts @@ -0,0 +1,55 @@ +// 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 { zoomState } from "./types"; +import { ZoomActionTypes, DASHBOARD_OPEN_ZOOM, DASHBOARD_CLOSE_ZOOM } from "./actions"; + +export interface DashboardState { + zoom: zoomState; +} + +const initialState: DashboardState = { + zoom: { + openZoom: false, + widgetRender: null, + }, +}; + +export function dashboardReducer( + state = initialState, + action: ZoomActionTypes +): DashboardState { + switch (action.type) { + case DASHBOARD_OPEN_ZOOM: + return { + ...state, + zoom: { + openZoom: true, + widgetRender: { ...action.widget }, + }, + }; + case DASHBOARD_CLOSE_ZOOM: + return { + ...state, + zoom: { + openZoom: false, + widgetRender: null, + }, + }; + default: + return state; + } +} diff --git a/portal-ui/src/screens/Console/Dashboard/types.tsx b/portal-ui/src/screens/Console/Dashboard/types.ts similarity index 89% rename from portal-ui/src/screens/Console/Dashboard/types.tsx rename to portal-ui/src/screens/Console/Dashboard/types.ts index c966cdeb2..09a7766a7 100644 --- a/portal-ui/src/screens/Console/Dashboard/types.tsx +++ b/portal-ui/src/screens/Console/Dashboard/types.ts @@ -14,6 +14,8 @@ // 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 Usage { usage: number; buckets: number; @@ -45,3 +47,8 @@ export interface IDriveInfo { usedSpace: number; availableSpace: number; } + +export interface zoomState { + openZoom: boolean, + widgetRender: null | IDashboardPanel, +} diff --git a/portal-ui/src/store.ts b/portal-ui/src/store.ts index 21fb933e1..0566eddb0 100644 --- a/portal-ui/src/store.ts +++ b/portal-ui/src/store.ts @@ -26,6 +26,7 @@ import { bucketsReducer } from "./screens/Console/Buckets/reducers"; import { objectBrowserReducer } from "./screens/Console/ObjectBrowser/reducers"; import { tenantsReducer } from "./screens/Console/Tenants/reducer"; import { directCSIReducer } from "./screens/Console/DirectCSI/reducer"; +import { dashboardReducer } from "./screens/Console/Dashboard/reducer"; const globalReducer = combineReducers({ system: systemReducer, @@ -38,6 +39,7 @@ const globalReducer = combineReducers({ healthInfo: healthInfoReducer, tenants: tenantsReducer, directCSI: directCSIReducer, + dashboard: dashboardReducer, }); declare global {