github.com/minio/console@v1.4.1/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/CapacityItem.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 styled from "styled-components";
    19  import get from "lodash/get";
    20  import { Box, breakPoints, Loader, ReportedUsageIcon } from "mds";
    21  import { Cell, Pie, PieChart } from "recharts";
    22  import { useSelector } from "react-redux";
    23  import api from "../../../../../common/api";
    24  import { IDashboardPanel } from "../types";
    25  import { widgetDetailsToPanel } from "../utils";
    26  import { ErrorResponseHandler } from "../../../../../common/types";
    27  import {
    28    calculateBytes,
    29    capacityColors,
    30    niceBytesInt,
    31  } from "../../../../../common/utils";
    32  import { setErrorSnackMessage } from "../../../../../systemSlice";
    33  import { AppState, useAppDispatch } from "../../../../../store";
    34  
    35  const CapacityItemMain = styled.div(({ theme }) => ({
    36    flex: 1,
    37    display: "flex",
    38    alignItems: "center",
    39    flexFlow: "row",
    40    "& .usableLabel": {
    41      color: get(theme, "mutedText", "#87888d"),
    42      fontSize: "10px",
    43      display: "flex",
    44      flexFlow: "column",
    45      alignItems: "center",
    46      textAlign: "center",
    47    },
    48    "& .usedLabel": {
    49      color: get(theme, "mutedText", "#87888d"),
    50      fontWeight: "bold",
    51      fontSize: "14px",
    52    },
    53    "& .totalUsed": {
    54      display: "flex",
    55      "& .value": {
    56        fontSize: "50px",
    57        fontFamily: "Inter",
    58        fontWeight: 600,
    59        alignSelf: "flex-end",
    60        lineHeight: 1,
    61      },
    62      "& .unit": {
    63        color: get(theme, "mutedText", "#87888d"),
    64        fontWeight: "bold",
    65        fontSize: "14px",
    66        marginLeft: "12px",
    67        alignSelf: "flex-end",
    68      },
    69    },
    70    "& .ofUsed": {
    71      marginTop: "5px",
    72      "& .value": {
    73        color: get(theme, "mutedText", "#87888d"),
    74        fontWeight: "bold",
    75        fontSize: "14px",
    76        textAlign: "right",
    77      },
    78    },
    79    [`@media (max-width: ${breakPoints.sm}px)`]: {
    80      flexFlow: "column",
    81    },
    82  }));
    83  
    84  const CapacityItem = ({
    85    value,
    86    timeStart,
    87    timeEnd,
    88    apiPrefix,
    89  }: {
    90    value: IDashboardPanel;
    91    timeStart: any;
    92    timeEnd: any;
    93    apiPrefix: string;
    94  }) => {
    95    const dispatch = useAppDispatch();
    96    const [loading, setLoading] = useState<boolean>(false);
    97  
    98    const [totalUsableFree, setTotalUsableFree] = useState<number>(0);
    99    const [totalUsableFreeRatio, setTotalUsableFreeRatio] = useState<number>(0);
   100    const [totalUsed, setTotalUsed] = useState<number>(0);
   101    const [totalUsable, setTotalUsable] = useState<number>(0);
   102    const widgetVersion = useSelector(
   103      (state: AppState) => state.dashboard.widgetLoadVersion,
   104    );
   105  
   106    useEffect(() => {
   107      setLoading(true);
   108    }, [widgetVersion]);
   109  
   110    useEffect(() => {
   111      if (loading) {
   112        let stepCalc = 0;
   113        if (timeStart !== null && timeEnd !== null) {
   114          const secondsInPeriod =
   115            timeEnd.toUnixInteger() - timeStart.toUnixInteger();
   116          const periods = Math.floor(secondsInPeriod / 60);
   117  
   118          stepCalc = periods < 1 ? 15 : periods;
   119        }
   120  
   121        api
   122          .invoke(
   123            "GET",
   124            `/api/v1/${apiPrefix}/info/widgets/${value.id}/?step=${stepCalc}&${
   125              timeStart !== null ? `&start=${timeStart.toUnixInteger()}` : ""
   126            }${timeStart !== null && timeEnd !== null ? "&" : ""}${
   127              timeEnd !== null ? `end=${timeEnd.toUnixInteger()}` : ""
   128            }`,
   129          )
   130          .then((res: any) => {
   131            const widgetsWithValue = widgetDetailsToPanel(res, value);
   132  
   133            let tUsable = 0;
   134            let tUsed = 0;
   135            let tFree = 0;
   136  
   137            widgetsWithValue.data.forEach((eachArray: any[]) => {
   138              eachArray.forEach((itemSum) => {
   139                switch (itemSum.legend) {
   140                  case "Total Usable":
   141                    tUsable += itemSum.value;
   142                    break;
   143                  case "Used Space":
   144                    tUsed += itemSum.value;
   145                    break;
   146                  case "Usable Free":
   147                    tFree += itemSum.value;
   148                    break;
   149                }
   150              });
   151            });
   152  
   153            const freeRatio = Math.round((tFree / tUsable) * 100);
   154  
   155            setTotalUsableFree(tFree);
   156            setTotalUsableFreeRatio(freeRatio);
   157            setTotalUsed(tUsed);
   158            setTotalUsable(tUsable);
   159  
   160            setLoading(false);
   161          })
   162          .catch((err: ErrorResponseHandler) => {
   163            dispatch(setErrorSnackMessage(err));
   164            setLoading(false);
   165          });
   166      }
   167    }, [loading, value, timeEnd, timeStart, dispatch, apiPrefix]);
   168  
   169    const usedConvert = calculateBytes(totalUsed, true, false);
   170  
   171    const plotValues = [
   172      {
   173        value: totalUsableFree,
   174        color: "#D6D6D6",
   175        label: "Usable Available Space",
   176      },
   177      {
   178        value: totalUsed,
   179        color: capacityColors(totalUsed, totalUsable),
   180        label: "Used Space",
   181      },
   182    ];
   183    return (
   184      <CapacityItemMain>
   185        <Box
   186          sx={{
   187            fontSize: "16px",
   188            fontWeight: 600,
   189            [`@media (max-width: ${breakPoints.sm}px)`]: {
   190              alignSelf: "flex-start",
   191            },
   192          }}
   193        >
   194          Capacity
   195        </Box>
   196        <Box
   197          sx={{
   198            position: "relative",
   199            width: 110,
   200            height: 110,
   201            marginLeft: "auto",
   202            [`@media (max-width: ${breakPoints.sm}px)`]: {
   203              marginLeft: "",
   204            },
   205          }}
   206        >
   207          <Box
   208            sx={{
   209              position: "absolute",
   210              display: "flex",
   211              flexFlow: "column",
   212              alignItems: "center",
   213              top: "50%",
   214              left: "50%",
   215              transform: "translate(-50%, -50%)",
   216              fontWeight: "bold",
   217              fontSize: 12,
   218            }}
   219          >
   220            {`${totalUsableFreeRatio}%`}
   221            <br />
   222            <Box className={"usableLabel"}>Free</Box>
   223          </Box>
   224          <PieChart width={110} height={110}>
   225            <Pie
   226              data={plotValues}
   227              cx={"50%"}
   228              cy={"50%"}
   229              dataKey="value"
   230              outerRadius={50}
   231              innerRadius={40}
   232              startAngle={-70}
   233              endAngle={360}
   234              animationDuration={1}
   235            >
   236              {plotValues.map((entry, index) => (
   237                <Cell key={`cellCapacity-${index}`} fill={entry.color} />
   238              ))}
   239            </Pie>
   240          </PieChart>
   241        </Box>
   242        <Box
   243          sx={{
   244            display: "flex",
   245            alignItems: "center",
   246            marginLeft: "auto",
   247            [`@media (max-width: ${breakPoints.sm}px)`]: {
   248              marginLeft: "",
   249            },
   250          }}
   251        >
   252          <Box>
   253            <Box className={"usedLabel"}>Used:</Box>
   254            <Box className={"totalUsed"}>
   255              <div className="value">{usedConvert.total}</div>
   256              <div className="unit">{usedConvert.unit}</div>
   257            </Box>
   258            <Box className={"ofUsed"}>
   259              <div className="value">Of: {niceBytesInt(totalUsable)}</div>
   260            </Box>
   261          </Box>
   262  
   263          <Box
   264            sx={{
   265              marginLeft: "15px",
   266              height: "100%",
   267              display: "flex",
   268              alignItems: "flex-start",
   269            }}
   270          >
   271            <Box>
   272              {loading ? (
   273                <Loader style={{ width: "26px", height: "26px" }} />
   274              ) : (
   275                <ReportedUsageIcon />
   276              )}
   277            </Box>
   278          </Box>
   279        </Box>
   280      </CapacityItemMain>
   281    );
   282  };
   283  
   284  export default CapacityItem;