github.com/minio/console@v1.4.1/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/PieChartWidget.tsx (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  import React, { useEffect, useState } from "react";
    18  import get from "lodash/get";
    19  import styled from "styled-components";
    20  import { Box, Loader } from "mds";
    21  import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts";
    22  import { useSelector } from "react-redux";
    23  import api from "../../../../../common/api";
    24  import { IPieChartConfiguration } from "./types";
    25  import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
    26  import { IDashboardPanel } from "../types";
    27  import { splitSizeMetric, widgetDetailsToPanel } from "../utils";
    28  import { ErrorResponseHandler } from "../../../../../common/types";
    29  import { setErrorSnackMessage } from "../../../../../systemSlice";
    30  import { AppState, useAppDispatch } from "../../../../../store";
    31  
    32  interface IPieChartWidget {
    33    title: string;
    34    panelItem: IDashboardPanel;
    35    timeStart: any;
    36    timeEnd: any;
    37    apiPrefix: string;
    38  }
    39  
    40  const PieChartMain = styled.div(({ theme }) => ({
    41    ...widgetCommon(theme),
    42    "& .loadingAlign": {
    43      width: "100%",
    44      paddingTop: "15px",
    45      textAlign: "center",
    46      margin: "auto",
    47    },
    48    "& .pieChartLabel": {
    49      fontSize: 60,
    50      color: get(theme, "signalColors.main", "#07193E"),
    51      fontWeight: "bold",
    52      width: "100%",
    53      "& .unitText": {
    54        color: get(theme, "mutedText", "#87888d"),
    55        fontSize: 12,
    56      },
    57    },
    58    "& .chartContainer": {
    59      width: "100%",
    60      height: 140,
    61    },
    62  }));
    63  
    64  const PieChartWidget = ({
    65    title,
    66    panelItem,
    67    timeStart,
    68    timeEnd,
    69    apiPrefix,
    70  }: IPieChartWidget) => {
    71    const dispatch = useAppDispatch();
    72    const [loading, setLoading] = useState<boolean>(false);
    73    const [dataInner, setDataInner] = useState<object[]>([]);
    74    const [dataOuter, setDataOuter] = useState<object[]>([]);
    75    const [result, setResult] = useState<IDashboardPanel | null>(null);
    76    const widgetVersion = useSelector(
    77      (state: AppState) => state.dashboard.widgetLoadVersion,
    78    );
    79  
    80    useEffect(() => {
    81      setLoading(true);
    82    }, [widgetVersion]);
    83  
    84    useEffect(() => {
    85      if (loading) {
    86        let stepCalc = 0;
    87        if (timeStart !== null && timeEnd !== null) {
    88          const secondsInPeriod =
    89            timeEnd.toUnixInteger() - timeStart.toUnixInteger();
    90          const periods = Math.floor(secondsInPeriod / 60);
    91  
    92          stepCalc = periods < 1 ? 15 : periods;
    93        }
    94  
    95        api
    96          .invoke(
    97            "GET",
    98            `/api/v1/${apiPrefix}/info/widgets/${
    99              panelItem.id
   100            }/?step=${stepCalc}&${
   101              timeStart !== null ? `&start=${timeStart.toUnixInteger()}` : ""
   102            }${timeStart !== null && timeEnd !== null ? "&" : ""}${
   103              timeEnd !== null ? `end=${timeEnd.toUnixInteger()}` : ""
   104            }`,
   105          )
   106          .then((res: any) => {
   107            const widgetsWithValue = widgetDetailsToPanel(res, panelItem);
   108            setDataInner(widgetsWithValue.data);
   109            setDataOuter(widgetsWithValue.dataOuter as object[]);
   110            setResult(widgetsWithValue);
   111            setLoading(false);
   112          })
   113          .catch((err: ErrorResponseHandler) => {
   114            dispatch(setErrorSnackMessage(err));
   115            setLoading(false);
   116          });
   117      }
   118    }, [loading, panelItem, timeEnd, timeStart, dispatch, apiPrefix]);
   119  
   120    const pieChartConfiguration = result
   121      ? (result.widgetConfiguration as IPieChartConfiguration)
   122      : [];
   123    const middleLabel = result?.innerLabel;
   124  
   125    const innerColors = get(pieChartConfiguration, "innerChart.colorList", []);
   126    const outerColors = get(pieChartConfiguration, "outerChart.colorList", []);
   127  
   128    return (
   129      <PieChartMain>
   130        <Box className={"singleValueContainer"}>
   131          <Box className={"titleContainer"}>{title}</Box>
   132          {loading && (
   133            <Box className={"loadingAlign"}>
   134              <Loader />
   135            </Box>
   136          )}
   137          {!loading && (
   138            <Box className={"contentContainer"}>
   139              <span className={"pieChartLabel"}>
   140                {middleLabel && splitSizeMetric(middleLabel)}
   141              </span>
   142              <Box className={"chartContainer"}>
   143                <ResponsiveContainer width="99%">
   144                  <PieChart margin={{ top: 5, bottom: 5 }}>
   145                    {dataOuter && (
   146                      <Pie
   147                        data={dataOuter as object[]}
   148                        cx={"50%"}
   149                        cy={"50%"}
   150                        dataKey="value"
   151                        innerRadius={get(
   152                          pieChartConfiguration,
   153                          "outerChart.innerRadius",
   154                          0,
   155                        )}
   156                        outerRadius={get(
   157                          pieChartConfiguration,
   158                          "outerChart.outerRadius",
   159                          "80%",
   160                        )}
   161                        startAngle={get(
   162                          pieChartConfiguration,
   163                          "outerChart.startAngle",
   164                          0,
   165                        )}
   166                        endAngle={get(
   167                          pieChartConfiguration,
   168                          "outerChart.endAngle",
   169                          360,
   170                        )}
   171                        fill="#201763"
   172                      >
   173                        {dataOuter.map((entry, index) => (
   174                          <Cell
   175                            key={`cellOuter-${index}`}
   176                            fill={
   177                              typeof outerColors[index] === "undefined"
   178                                ? "#393939"
   179                                : outerColors[index]
   180                            }
   181                          />
   182                        ))}
   183                      </Pie>
   184                    )}
   185                    {dataInner && (
   186                      <Pie
   187                        data={dataInner as object[]}
   188                        dataKey="value"
   189                        cx={"50%"}
   190                        cy={"50%"}
   191                        innerRadius={get(
   192                          pieChartConfiguration,
   193                          "innerChart.innerRadius",
   194                          0,
   195                        )}
   196                        outerRadius={get(
   197                          pieChartConfiguration,
   198                          "innerChart.outerRadius",
   199                          "80%",
   200                        )}
   201                        startAngle={get(
   202                          pieChartConfiguration,
   203                          "innerChart.startAngle",
   204                          0,
   205                        )}
   206                        endAngle={get(
   207                          pieChartConfiguration,
   208                          "innerChart.endAngle",
   209                          360,
   210                        )}
   211                        fill="#201763"
   212                      >
   213                        {dataInner.map((entry, index) => {
   214                          return (
   215                            <Cell
   216                              key={`cell-${index}`}
   217                              fill={
   218                                typeof innerColors[index] === "undefined"
   219                                  ? "#393939"
   220                                  : innerColors[index]
   221                              }
   222                            />
   223                          );
   224                        })}
   225                      </Pie>
   226                    )}
   227                  </PieChart>
   228                </ResponsiveContainer>
   229              </Box>
   230            </Box>
   231          )}
   232        </Box>
   233      </PieChartMain>
   234    );
   235  };
   236  
   237  export default PieChartWidget;