github.com/minio/console@v1.4.1/web-app/src/screens/Console/Dashboard/Prometheus/PrDashboard.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, { Fragment, useState } from "react";
    18  import {
    19    Box,
    20    Button,
    21    Grid,
    22    HelpBox,
    23    PageLayout,
    24    ProgressBar,
    25    PrometheusErrorIcon,
    26    SyncIcon,
    27    TabItemProps,
    28    Tabs,
    29  } from "mds";
    30  import { IDashboardPanel } from "./types";
    31  import { panelsConfiguration } from "./utils";
    32  import { componentToUse } from "./widgetUtils";
    33  import { useAppDispatch, useAppSelector } from "../../../../store";
    34  import {
    35    DLayoutColumnProps,
    36    DLayoutRowProps,
    37    resourcesPanelsLayout,
    38    resourcesPanelsLayoutAdvanced,
    39    RowPanelLayout,
    40    summaryPanelsLayout,
    41    trafficPanelsLayout,
    42  } from "./Widgets/LayoutUtil";
    43  import { getUsageAsync } from "../dashboardThunks";
    44  import { reloadWidgets } from "../dashboardSlice";
    45  import { selFeatures } from "../../consoleSlice";
    46  import { AdminInfoResponse } from "api/consoleApi";
    47  import ZoomWidget from "./ZoomWidget";
    48  import DateRangeSelector from "../../Common/FormComponents/DateRangeSelector/DateRangeSelector";
    49  import MergedWidgetsRenderer from "./Widgets/MergedWidgetsRenderer";
    50  import BasicDashboard from "../BasicDashboard/BasicDashboard";
    51  
    52  interface IPrDashboard {
    53    apiPrefix?: string;
    54    usage: AdminInfoResponse | null;
    55  }
    56  
    57  const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => {
    58    const dispatch = useAppDispatch();
    59    const status = useAppSelector((state) => state.dashboard.status);
    60    const zoomOpen = useAppSelector((state) => state.dashboard.zoom.openZoom);
    61    const zoomWidget = useAppSelector(
    62      (state) => state.dashboard.zoom.widgetRender,
    63    );
    64    const features = useAppSelector(selFeatures);
    65    const obOnly = !!features?.includes("object-browser-only");
    66    let hideMenu = false;
    67    if (features?.includes("hide-menu")) {
    68      hideMenu = true;
    69    } else if (obOnly) {
    70      hideMenu = true;
    71    }
    72  
    73    const [timeStart, setTimeStart] = useState<any>(null);
    74    const [timeEnd, setTimeEnd] = useState<any>(null);
    75    const panelInformation = panelsConfiguration;
    76    const [curTab, setCurTab] = useState<string>("info");
    77  
    78    const getPanelDetails = (id: number) => {
    79      return panelInformation.find((panel) => panel.id === id);
    80    };
    81  
    82    const triggerLoad = () => {
    83      dispatch(reloadWidgets());
    84    };
    85  
    86    const renderCmpByConfig = (
    87      panelInfo: IDashboardPanel | undefined,
    88      key: string,
    89    ) => {
    90      return (
    91        <Fragment key={`widget-${key}`}>
    92          {panelInfo ? (
    93            <Fragment>
    94              <Box>
    95                {panelInfo.mergedPanels ? (
    96                  <MergedWidgetsRenderer
    97                    info={panelInfo}
    98                    timeStart={timeStart}
    99                    timeEnd={timeEnd}
   100                    loading={true}
   101                    apiPrefix={apiPrefix}
   102                  />
   103                ) : (
   104                  componentToUse(
   105                    panelInfo,
   106                    timeStart,
   107                    timeEnd,
   108                    true,
   109                    apiPrefix,
   110                    zoomOpen,
   111                  )
   112                )}
   113              </Box>
   114            </Fragment>
   115          ) : null}
   116        </Fragment>
   117      );
   118    };
   119  
   120    const renderPanelItems = (layoutRows: DLayoutRowProps[]) => {
   121      return layoutRows.reduce((prev: any[], rowItem, rIdx) => {
   122        const { columns = [] } = rowItem;
   123        const cellItems: any[] = columns.map(
   124          (cellItem: DLayoutColumnProps, colIdx: number) => {
   125            const panelInfo = getPanelDetails(cellItem.componentId);
   126            return renderCmpByConfig(panelInfo, `${rIdx}-${colIdx}`);
   127          },
   128        );
   129        const rowConfig = (
   130          <Box sx={rowItem.sx} key={`layout-row-${rIdx}`}>
   131            {cellItems}
   132          </Box>
   133        );
   134        return [...prev, rowConfig];
   135      }, []);
   136    };
   137  
   138    const renderSummaryPanels = () => {
   139      return renderPanelItems(summaryPanelsLayout);
   140    };
   141  
   142    const renderTrafficPanels = () => {
   143      return renderPanelItems(trafficPanelsLayout);
   144    };
   145  
   146    const renderResourcesPanels = () => {
   147      return renderPanelItems(resourcesPanelsLayout);
   148    };
   149  
   150    const renderAdvancedResourcesPanels = () => {
   151      return renderPanelItems(resourcesPanelsLayoutAdvanced);
   152    };
   153  
   154    const prometheusOptionsDisabled =
   155      usage?.advancedMetricsStatus === "not configured";
   156  
   157    const searchBox = (
   158      <Box sx={{ marginBottom: 20 }}>
   159        {curTab === "info" ? (
   160          <Grid container>
   161            <Grid item>
   162              <Box
   163                sx={{
   164                  fontSize: 18,
   165                  lineHeight: 2,
   166                  fontWeight: 700,
   167                }}
   168              >
   169                Server Information
   170              </Box>
   171            </Grid>
   172            <Grid item xs>
   173              <Grid container direction="row-reverse">
   174                <Grid item>
   175                  <Button
   176                    id={"sync"}
   177                    type="button"
   178                    variant="callAction"
   179                    onClick={() => {
   180                      dispatch(getUsageAsync());
   181                    }}
   182                    disabled={status === "loading"}
   183                    icon={<SyncIcon />}
   184                    label={"Sync"}
   185                  />
   186                </Grid>
   187              </Grid>
   188            </Grid>
   189          </Grid>
   190        ) : (
   191          <DateRangeSelector
   192            timeStart={timeStart}
   193            setTimeStart={setTimeStart}
   194            timeEnd={timeEnd}
   195            setTimeEnd={setTimeEnd}
   196            triggerSync={triggerLoad}
   197          />
   198        )}
   199      </Box>
   200    );
   201  
   202    const infoTab: TabItemProps = {
   203      tabConfig: { label: "Info", id: "info", disabled: false },
   204      content: (
   205        <Fragment>
   206          {(!usage || status === "loading") && <ProgressBar />}
   207          {usage && status === "idle" && (
   208            <Fragment>
   209              {searchBox}
   210              <BasicDashboard usage={usage} />
   211            </Fragment>
   212          )}
   213        </Fragment>
   214      ),
   215    };
   216  
   217    const prometheusTabs: TabItemProps[] = [
   218      {
   219        tabConfig: {
   220          label: "Usage",
   221          id: "usage",
   222          disabled: prometheusOptionsDisabled,
   223        },
   224        content: (
   225          <Fragment>
   226            {searchBox}
   227            <RowPanelLayout>
   228              {usage?.advancedMetricsStatus === "unavailable" && (
   229                <HelpBox
   230                  iconComponent={<PrometheusErrorIcon />}
   231                  title={"We can’t retrieve advanced metrics at this time."}
   232                  help={
   233                    <Box
   234                      sx={{
   235                        fontSize: "14px",
   236                      }}
   237                    >
   238                      It looks like Prometheus is not available or reachable at
   239                      the moment.
   240                    </Box>
   241                  }
   242                />
   243              )}
   244              {panelInformation.length ? renderSummaryPanels() : null}
   245            </RowPanelLayout>
   246          </Fragment>
   247        ),
   248      },
   249      {
   250        tabConfig: {
   251          label: "Traffic",
   252          id: "traffic",
   253          disabled: prometheusOptionsDisabled,
   254        },
   255        content: (
   256          <Fragment>
   257            {searchBox}
   258            <RowPanelLayout>
   259              {usage?.advancedMetricsStatus === "unavailable" && (
   260                <HelpBox
   261                  iconComponent={<PrometheusErrorIcon />}
   262                  title={"We can’t retrieve advanced metrics at this time."}
   263                  help={
   264                    <Box
   265                      sx={{
   266                        fontSize: "14px",
   267                      }}
   268                    >
   269                      It looks like Prometheus is not available or reachable at
   270                      the moment.
   271                    </Box>
   272                  }
   273                />
   274              )}
   275              {panelInformation.length ? renderTrafficPanels() : null}
   276            </RowPanelLayout>
   277          </Fragment>
   278        ),
   279      },
   280      {
   281        tabConfig: {
   282          label: "Resources",
   283          id: "resources",
   284          disabled: prometheusOptionsDisabled,
   285        },
   286        content: (
   287          <Fragment>
   288            {searchBox}
   289            <RowPanelLayout>
   290              {usage?.advancedMetricsStatus === "unavailable" && (
   291                <HelpBox
   292                  iconComponent={<PrometheusErrorIcon />}
   293                  title={"We can’t retrieve advanced metrics at this time."}
   294                  help={
   295                    <Box
   296                      sx={{
   297                        fontSize: "14px",
   298                      }}
   299                    >
   300                      It looks like Prometheus is not available or reachable at
   301                      the moment.
   302                    </Box>
   303                  }
   304                />
   305              )}
   306              {panelInformation.length ? renderResourcesPanels() : null}
   307              <h2 style={{ margin: 0, borderBottom: "1px solid #dedede" }}>
   308                Advanced
   309              </h2>
   310              {panelInformation.length ? renderAdvancedResourcesPanels() : null}
   311            </RowPanelLayout>
   312          </Fragment>
   313        ),
   314      },
   315    ];
   316  
   317    let tabsOptions: TabItemProps[] = [infoTab, ...prometheusTabs];
   318  
   319    return (
   320      <PageLayout
   321        sx={{
   322          padding: hideMenu ? 0 : "2rem",
   323        }}
   324      >
   325        {zoomOpen && (
   326          <ZoomWidget
   327            modalOpen={zoomOpen}
   328            timeStart={timeStart}
   329            timeEnd={timeEnd}
   330            widgetRender={0}
   331            value={zoomWidget}
   332            apiPrefix={apiPrefix}
   333          />
   334        )}
   335  
   336        <Tabs
   337          horizontal
   338          options={tabsOptions}
   339          currentTabOrPath={curTab}
   340          onTabClick={(newValue) => {
   341            setCurTab(newValue);
   342          }}
   343        />
   344      </PageLayout>
   345    );
   346  };
   347  
   348  export default PrDashboard;